machinevision-toolbox-python 2.2.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.2.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/METADATA +18 -14
- {machinevision_toolbox_python-2.2.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/RECORD +21 -21
- {machinevision_toolbox_python-2.2.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/WHEEL +1 -1
- 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.2.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/entry_points.txt +0 -0
- {machinevision_toolbox_python-2.2.0.dist-info → machinevision_toolbox_python-2.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -22,7 +22,7 @@ import numpy as np
|
|
|
22
22
|
|
|
23
23
|
from machinevisiontoolbox.base.color import colorspace_convert, gamma_decode
|
|
24
24
|
from machinevisiontoolbox.base.data import mvtb_path_to_datafile
|
|
25
|
-
from machinevisiontoolbox.base.types import float_image, int_image
|
|
25
|
+
from machinevisiontoolbox.base.types import DTYPE_ALIASES, float_image, int_image
|
|
26
26
|
|
|
27
27
|
try:
|
|
28
28
|
import pyclip
|
|
@@ -456,470 +456,575 @@ def idisp(
|
|
|
456
456
|
# if we are running in a Jupyter notebook, print to matplotlib,
|
|
457
457
|
# otherwise print to opencv imshow/new window. This is done because
|
|
458
458
|
# cv2.imshow does not play nicely with .ipynb
|
|
459
|
-
if matplotlib:
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
title
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
459
|
+
if matplotlib:
|
|
460
|
+
return _idisp_matplotlib(
|
|
461
|
+
im,
|
|
462
|
+
colororder,
|
|
463
|
+
block=block,
|
|
464
|
+
fps=fps,
|
|
465
|
+
fig=fig,
|
|
466
|
+
ax=ax,
|
|
467
|
+
reuse=reuse,
|
|
468
|
+
colormap=colormap,
|
|
469
|
+
ncolors=ncolors,
|
|
470
|
+
black=black,
|
|
471
|
+
darken=darken,
|
|
472
|
+
powernorm=powernorm,
|
|
473
|
+
gamma=gamma,
|
|
474
|
+
vrange=vrange,
|
|
475
|
+
badcolor=badcolor,
|
|
476
|
+
undercolor=undercolor,
|
|
477
|
+
overcolor=overcolor,
|
|
478
|
+
title=title,
|
|
479
|
+
grid=grid,
|
|
480
|
+
axes=axes,
|
|
481
|
+
axlabels=axlabels,
|
|
482
|
+
gui=gui,
|
|
483
|
+
frame=frame,
|
|
484
|
+
colorbar=colorbar,
|
|
485
|
+
square=square,
|
|
486
|
+
width=width,
|
|
487
|
+
height=height,
|
|
488
|
+
ynormal=ynormal,
|
|
489
|
+
extent=extent,
|
|
490
|
+
coordformat=coordformat,
|
|
491
|
+
savefigname=savefigname,
|
|
492
|
+
**kwargs,
|
|
493
|
+
)
|
|
494
|
+
else:
|
|
495
|
+
return _idisp_opencv(
|
|
496
|
+
im, colororder, title=title, block=block, fps=fps, reuse=reuse
|
|
497
|
+
)
|
|
490
498
|
|
|
491
|
-
# look for an image in the axes to update, if there is one
|
|
492
|
-
updated = False
|
|
493
|
-
for c in ax.get_children():
|
|
494
|
-
if isinstance(c, mpl.image.AxesImage):
|
|
495
|
-
c.set_data(im)
|
|
496
|
-
updated = True
|
|
497
|
-
if updated:
|
|
498
|
-
set_window_title(title)
|
|
499
499
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
500
|
+
def _idisp_matplotlib(
|
|
501
|
+
im: np.ndarray,
|
|
502
|
+
colororder: str = "RGB",
|
|
503
|
+
block: bool | float | None = None,
|
|
504
|
+
fps: float | None = None,
|
|
505
|
+
fig: Any = None,
|
|
506
|
+
ax: Any = None,
|
|
507
|
+
reuse: bool = False,
|
|
508
|
+
colormap: str | Any = None,
|
|
509
|
+
ncolors: int | None = None,
|
|
510
|
+
black: int | float = 0,
|
|
511
|
+
darken: float | bool | None = None,
|
|
512
|
+
powernorm: bool = False,
|
|
513
|
+
gamma: float | None = None,
|
|
514
|
+
vrange: tuple[float, float] | list[float] | None = None,
|
|
515
|
+
badcolor: str | None = None,
|
|
516
|
+
undercolor: str | None = None,
|
|
517
|
+
overcolor: str | None = None,
|
|
518
|
+
title: str | None = None,
|
|
519
|
+
grid: bool = False,
|
|
520
|
+
axes: bool = True,
|
|
521
|
+
axlabels: tuple[str, str] | list[str] = ("u (pixels)", "v (pixels)"),
|
|
522
|
+
gui: bool = True,
|
|
523
|
+
frame: bool = True,
|
|
524
|
+
colorbar: bool | dict[str, Any] = False,
|
|
525
|
+
square: bool = True,
|
|
526
|
+
width: float | None = None,
|
|
527
|
+
height: float | None = None,
|
|
528
|
+
ynormal: bool = False,
|
|
529
|
+
extent: tuple[float, float, float, float] | list[float] | None = None,
|
|
530
|
+
coordformat: Callable[[float, float], str] | None = None,
|
|
531
|
+
savefigname: str | None = None,
|
|
532
|
+
**kwargs: Any,
|
|
533
|
+
) -> Any:
|
|
534
|
+
"""Matplotlib backend for :func:`idisp`.
|
|
503
535
|
|
|
504
|
-
|
|
536
|
+
Not part of the public API -- called from :func:`idisp` when
|
|
537
|
+
``matplotlib=True``. Split out so each display backend's logic can be
|
|
538
|
+
read, tested and fixed independently of the other; see :func:`idisp`
|
|
539
|
+
for the full parameter documentation.
|
|
505
540
|
|
|
506
|
-
|
|
541
|
+
:return: Matplotlib ``AxesImage`` handle
|
|
542
|
+
:rtype: matplotlib.image.AxesImage
|
|
543
|
+
"""
|
|
544
|
+
## display using matplotlib
|
|
545
|
+
plt = _plt()
|
|
507
546
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
547
|
+
# if flatten:
|
|
548
|
+
# # either make new subplots for each channel
|
|
549
|
+
# # or concatenate all into one large image and display
|
|
550
|
+
# # TODO can we make axes as a list?
|
|
551
|
+
|
|
552
|
+
# # for now, just concatenate:
|
|
553
|
+
# # first check how many channels:
|
|
554
|
+
# if im.ndim > 2:
|
|
555
|
+
# # create list of image channels
|
|
556
|
+
# imcl = [im[:, :, i] for i in range(im.shape[2])]
|
|
557
|
+
# # stack horizontally
|
|
558
|
+
# im = np.hstack(imcl)
|
|
559
|
+
# # else just plot the regular image - only one channel
|
|
560
|
+
|
|
561
|
+
if title is None:
|
|
562
|
+
title = "Machine Vision Toolbox for Python"
|
|
563
|
+
|
|
564
|
+
if len(plt.get_fignums()) == 0:
|
|
565
|
+
# there are no figures, create one
|
|
566
|
+
fig, ax = plt.subplots() # fig creates a new window
|
|
567
|
+
else:
|
|
568
|
+
# there are existing figures
|
|
511
569
|
|
|
570
|
+
if reuse:
|
|
571
|
+
# attempt to reuse an axes, saves all the setup overhead
|
|
512
572
|
if ax is None:
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
if fig is None:
|
|
516
|
-
fig = ax.figure
|
|
517
|
-
|
|
518
|
-
set_window_title(title)
|
|
519
|
-
# aspect ratio:
|
|
520
|
-
if not square:
|
|
521
|
-
mpl.rcParams["image.aspect"] = "auto"
|
|
522
|
-
|
|
523
|
-
# hide interactive toolbar buttons (must be before figure creation)
|
|
524
|
-
if not gui:
|
|
525
|
-
mpl.rcParams["toolbar"] = "None"
|
|
526
|
-
|
|
527
|
-
if darken is True:
|
|
528
|
-
darken = 0.5
|
|
529
|
-
|
|
530
|
-
# # experiment with addign buttons to the navigation bar
|
|
531
|
-
# matplotlib.rcParams["toolbar"] = "toolmanager"
|
|
532
|
-
# class LineTool(ToolToggleBase):
|
|
533
|
-
|
|
534
|
-
# def trigger(self, *args, **kwargs):
|
|
535
|
-
# print('hello from trigger')
|
|
536
|
-
|
|
537
|
-
# tm = fig.canvas.manager.toolmanager
|
|
538
|
-
# tm.add_tool('newtool', LineTool)
|
|
539
|
-
# fig.canvas.manager.toolbar.add_tool(tm.get_tool("newtool"), "toolgroup")
|
|
540
|
-
|
|
541
|
-
# get screen resolution:
|
|
542
|
-
# swidth, sheight = pyautogui.size() # pixels TODO REPLACE THIS WITH STUFF FROM BDSIM
|
|
543
|
-
|
|
544
|
-
# mpl_backend = mpl.get_backend()
|
|
545
|
-
|
|
546
|
-
# if mpl_backend == 'Qt5Agg':
|
|
547
|
-
# from PyQt5 import QtWidgets
|
|
548
|
-
# app = QtWidgets.QApplication([])
|
|
549
|
-
# screen = app.primaryScreen()
|
|
550
|
-
# if screen.name is not None:
|
|
551
|
-
# print(' Screen: %s' % screen.name())
|
|
552
|
-
# size = screen.size()
|
|
553
|
-
# print(' Size: %d x %d' % (size.width(), size.height()))
|
|
554
|
-
# rect = screen.availableGeometry()
|
|
555
|
-
# print(' Available: %d x %d' % (rect.width(), rect.height()))
|
|
556
|
-
# sw = rect.width()
|
|
557
|
-
# sh = rect.height()
|
|
558
|
-
# #dpi = screen.physicalDotsPerInch()
|
|
559
|
-
# dpiscale = screen.devicePixelRatio() # is 2.0 for Mac laptop screen
|
|
560
|
-
# elif mpl_backend == 'TkAgg':
|
|
561
|
-
# window = plt.get_current_fig_manager().window
|
|
562
|
-
# sw = window.winfo_screenwidth()
|
|
563
|
-
# sh = window.winfo_screenheight()
|
|
564
|
-
# print(' Size: %d x %d' % (sw, sh))
|
|
565
|
-
# else:
|
|
566
|
-
# print('unknown backend, can't find width', mpl_backend)
|
|
567
|
-
|
|
568
|
-
# dpi = None # can make this an input option
|
|
569
|
-
# if dpi is None:
|
|
570
|
-
# dpi = mpl.rcParams['figure.dpi'] # default is 100
|
|
571
|
-
|
|
572
|
-
if width is not None:
|
|
573
|
-
fig.set_figwidth(width / 25.4) # inches
|
|
574
|
-
|
|
575
|
-
if height is not None:
|
|
576
|
-
fig.set_figheight(height / 25.4) # inches
|
|
577
|
-
|
|
578
|
-
## Create the colormap and normalizer
|
|
579
|
-
norm = None
|
|
580
|
-
cmap = None
|
|
581
|
-
if colormap == "invert":
|
|
582
|
-
cmap = "Greys"
|
|
583
|
-
elif colormap == "signed":
|
|
584
|
-
# signed color map, red is negative, blue is positive, zero is white
|
|
585
|
-
cmap = "bwr_r" # blue -> white -> red
|
|
586
|
-
min = np.min(im)
|
|
587
|
-
max = np.max(im)
|
|
588
|
-
|
|
589
|
-
# ensure min/max are symmetric about zero, so that zero is white
|
|
590
|
-
if abs(max) >= abs(min):
|
|
591
|
-
min = -max # lgtm[py/multiple-definition]
|
|
592
|
-
else:
|
|
593
|
-
max = -min # lgtm[py/multiple-definition]
|
|
573
|
+
ax = plt.gca()
|
|
594
574
|
|
|
595
|
-
if
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
elif colormap == "invsigned":
|
|
604
|
-
# inverse signed color map, red is negative, blue is positive, zero is black
|
|
605
|
-
cdict = {
|
|
606
|
-
"red": [(0, 1, 1), (0.5, 0, 0), (1, 0, 0)],
|
|
607
|
-
"green": [(0, 0, 0), (1, 0, 0)],
|
|
608
|
-
"blue": [(0, 0, 0), (0.5, 0, 0), (1, 1, 1)],
|
|
609
|
-
}
|
|
610
|
-
if ncolors is None:
|
|
611
|
-
cmap = mpl.colors.LinearSegmentedColormap("signed", cdict)
|
|
612
|
-
else:
|
|
613
|
-
cmap = mpl.colors.LinearSegmentedColormap("signed", cdict, N=ncolors)
|
|
614
|
-
min = np.min(im)
|
|
615
|
-
max = np.max(im)
|
|
575
|
+
# look for an image in the axes to update, if there is one
|
|
576
|
+
updated = False
|
|
577
|
+
for c in ax.get_children():
|
|
578
|
+
if isinstance(c, mpl.image.AxesImage):
|
|
579
|
+
c.set_data(im)
|
|
580
|
+
updated = True
|
|
581
|
+
if updated:
|
|
582
|
+
set_window_title(title)
|
|
616
583
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
else:
|
|
621
|
-
max = -min
|
|
584
|
+
if fps is not None:
|
|
585
|
+
# print("pausing", 1.0 / fps)
|
|
586
|
+
plt.pause(1.0 / fps)
|
|
622
587
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
588
|
+
safe_plt_show(block=block)
|
|
589
|
+
|
|
590
|
+
return
|
|
591
|
+
|
|
592
|
+
if fig is not None:
|
|
593
|
+
# make this figure the current one
|
|
594
|
+
plt.figure(fig)
|
|
595
|
+
|
|
596
|
+
if ax is None:
|
|
597
|
+
fig, ax = plt.subplots() # fig creates a new window
|
|
598
|
+
|
|
599
|
+
if fig is None:
|
|
600
|
+
fig = ax.figure
|
|
601
|
+
|
|
602
|
+
set_window_title(title)
|
|
603
|
+
# aspect ratio:
|
|
604
|
+
if not square:
|
|
605
|
+
mpl.rcParams["image.aspect"] = "auto"
|
|
606
|
+
|
|
607
|
+
# hide interactive toolbar buttons (must be before figure creation)
|
|
608
|
+
if not gui:
|
|
609
|
+
mpl.rcParams["toolbar"] = "None"
|
|
610
|
+
|
|
611
|
+
if darken is True:
|
|
612
|
+
darken = 0.5
|
|
613
|
+
|
|
614
|
+
# # experiment with addign buttons to the navigation bar
|
|
615
|
+
# matplotlib.rcParams["toolbar"] = "toolmanager"
|
|
616
|
+
# class LineTool(ToolToggleBase):
|
|
617
|
+
|
|
618
|
+
# def trigger(self, *args, **kwargs):
|
|
619
|
+
# print('hello from trigger')
|
|
620
|
+
|
|
621
|
+
# tm = fig.canvas.manager.toolmanager
|
|
622
|
+
# tm.add_tool('newtool', LineTool)
|
|
623
|
+
# fig.canvas.manager.toolbar.add_tool(tm.get_tool("newtool"), "toolgroup")
|
|
624
|
+
|
|
625
|
+
# get screen resolution:
|
|
626
|
+
# swidth, sheight = pyautogui.size() # pixels TODO REPLACE THIS WITH STUFF FROM BDSIM
|
|
627
|
+
|
|
628
|
+
# mpl_backend = mpl.get_backend()
|
|
629
|
+
|
|
630
|
+
# if mpl_backend == 'Qt5Agg':
|
|
631
|
+
# from PyQt5 import QtWidgets
|
|
632
|
+
# app = QtWidgets.QApplication([])
|
|
633
|
+
# screen = app.primaryScreen()
|
|
634
|
+
# if screen.name is not None:
|
|
635
|
+
# print(' Screen: %s' % screen.name())
|
|
636
|
+
# size = screen.size()
|
|
637
|
+
# print(' Size: %d x %d' % (size.width(), size.height()))
|
|
638
|
+
# rect = screen.availableGeometry()
|
|
639
|
+
# print(' Available: %d x %d' % (rect.width(), rect.height()))
|
|
640
|
+
# sw = rect.width()
|
|
641
|
+
# sh = rect.height()
|
|
642
|
+
# #dpi = screen.physicalDotsPerInch()
|
|
643
|
+
# dpiscale = screen.devicePixelRatio() # is 2.0 for Mac laptop screen
|
|
644
|
+
# elif mpl_backend == 'TkAgg':
|
|
645
|
+
# window = plt.get_current_fig_manager().window
|
|
646
|
+
# sw = window.winfo_screenwidth()
|
|
647
|
+
# sh = window.winfo_screenheight()
|
|
648
|
+
# print(' Size: %d x %d' % (sw, sh))
|
|
649
|
+
# else:
|
|
650
|
+
# print('unknown backend, can't find width', mpl_backend)
|
|
651
|
+
|
|
652
|
+
# dpi = None # can make this an input option
|
|
653
|
+
# if dpi is None:
|
|
654
|
+
# dpi = mpl.rcParams['figure.dpi'] # default is 100
|
|
655
|
+
|
|
656
|
+
if width is not None:
|
|
657
|
+
fig.set_figwidth(width / 25.4) # inches
|
|
658
|
+
|
|
659
|
+
if height is not None:
|
|
660
|
+
fig.set_figheight(height / 25.4) # inches
|
|
661
|
+
|
|
662
|
+
## Create the colormap and normalizer
|
|
663
|
+
norm = None
|
|
664
|
+
cmap = None
|
|
665
|
+
if colormap == "invert":
|
|
666
|
+
cmap = "Greys"
|
|
667
|
+
elif colormap == "signed":
|
|
668
|
+
# signed color map, red is negative, blue is positive, zero is white
|
|
669
|
+
cmap = "bwr_r" # blue -> white -> red
|
|
670
|
+
min = np.min(im)
|
|
671
|
+
max = np.max(im)
|
|
672
|
+
|
|
673
|
+
# ensure min/max are symmetric about zero, so that zero is white
|
|
674
|
+
if abs(max) >= abs(min):
|
|
675
|
+
min = -max # lgtm[py/multiple-definition]
|
|
635
676
|
else:
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
if im.ndim == 3 and darken is not None:
|
|
644
|
-
im = float_image(im) / darken
|
|
645
|
-
|
|
646
|
-
if isinstance(cmap, str):
|
|
647
|
-
# cmap = cm.get_cmap(cmap, lut=ncolors)
|
|
648
|
-
cmap = mpl.colormaps[cmap]
|
|
649
|
-
if ncolors is not None:
|
|
650
|
-
cmap = cmap.resampled(ncolors)
|
|
651
|
-
|
|
652
|
-
# handle values outside of range
|
|
653
|
-
#
|
|
654
|
-
# - undercolor, below vmin
|
|
655
|
-
# - overcolor, above vmax
|
|
656
|
-
# - badcolor, nan, -inf, inf
|
|
657
|
-
#
|
|
658
|
-
# only works for greyscale image
|
|
659
|
-
if im.ndim == 2:
|
|
660
|
-
cmap = copy.copy(cmap)
|
|
661
|
-
if undercolor is not None:
|
|
662
|
-
cmap.set_under(color=undercolor)
|
|
663
|
-
if overcolor is not None:
|
|
664
|
-
cmap.set_over(color=overcolor)
|
|
665
|
-
if badcolor is not None:
|
|
666
|
-
cmap.set_bad(color=badcolor)
|
|
667
|
-
# elif im.ndim == 3:
|
|
668
|
-
# if badcolor is not None:
|
|
669
|
-
# cmap.set_bad(color=badcolor)
|
|
670
|
-
|
|
671
|
-
if black != 0:
|
|
672
|
-
if np.issubdtype(im.dtype, np.floating):
|
|
673
|
-
m = 1 - black
|
|
674
|
-
c = black
|
|
675
|
-
im = m * im + c
|
|
676
|
-
norm = mpl.colors.Normalize(0, 1)
|
|
677
|
-
elif np.issubdtype(im.dtype, bool):
|
|
678
|
-
norm = mpl.colors.Normalize(0, 1)
|
|
679
|
-
ncolors = 2
|
|
680
|
-
else:
|
|
681
|
-
max = np.iinfo(im.dtype).max
|
|
682
|
-
black = black * max
|
|
683
|
-
c = black
|
|
684
|
-
m = (max - c) / max
|
|
685
|
-
im = (m * im + c).astype(im.dtype)
|
|
686
|
-
norm = mpl.colors.Normalize(0, max)
|
|
677
|
+
max = -min # lgtm[py/multiple-definition]
|
|
678
|
+
|
|
679
|
+
if powernorm:
|
|
680
|
+
norm = mpl.colors.PowerNorm(gamma=0.45)
|
|
681
|
+
else:
|
|
682
|
+
# if abs(min) > abs(max):
|
|
683
|
+
# norm = mpl.colors.Normalize(vmin=min, vmax=abs(min / max) * max)
|
|
687
684
|
# else:
|
|
688
|
-
#
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
# build up options for imshow
|
|
701
|
-
options = kwargs
|
|
702
|
-
if ynormal:
|
|
703
|
-
options["origin"] = "lower"
|
|
704
|
-
|
|
705
|
-
if extent is not None:
|
|
706
|
-
options["extent"] = extent
|
|
707
|
-
|
|
708
|
-
# display the image
|
|
709
|
-
if len(im.shape) == 3:
|
|
710
|
-
# color image, display in RGB or RGBA order; imshow handles alpha
|
|
711
|
-
# natively for both uint8 (0-255) and float (0.0-1.0) arrays
|
|
712
|
-
target = "RGBA" if "A" in colororder else "RGB"
|
|
713
|
-
idx = [colororder.find(c) for c in target if colororder.find(c) >= 0]
|
|
714
|
-
im_rgb = im[:, :, idx] # reorder planes → RGB or RGBA
|
|
715
|
-
h = ax.imshow(im_rgb, norm=norm, cmap=cmap, **options)
|
|
685
|
+
# norm = mpl.colors.Normalize(vmin=abs(max / min) * min, vmax=max)
|
|
686
|
+
norm = mpl.colors.CenteredNorm()
|
|
687
|
+
elif colormap == "invsigned":
|
|
688
|
+
# inverse signed color map, red is negative, blue is positive, zero is black
|
|
689
|
+
cdict = {
|
|
690
|
+
"red": [(0, 1, 1), (0.5, 0, 0), (1, 0, 0)],
|
|
691
|
+
"green": [(0, 0, 0), (1, 0, 0)],
|
|
692
|
+
"blue": [(0, 0, 0), (0.5, 0, 0), (1, 1, 1)],
|
|
693
|
+
}
|
|
694
|
+
if ncolors is None:
|
|
695
|
+
cmap = mpl.colors.LinearSegmentedColormap("signed", cdict)
|
|
716
696
|
else:
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
norm = mpl.colors.Normalize(vmin=min, vmax=max)
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
if
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
#
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
697
|
+
cmap = mpl.colors.LinearSegmentedColormap("signed", cdict, N=ncolors)
|
|
698
|
+
min = np.min(im)
|
|
699
|
+
max = np.max(im)
|
|
700
|
+
|
|
701
|
+
# ensure min/max are symmetric about zero, so that zero is black
|
|
702
|
+
if abs(max) >= abs(min):
|
|
703
|
+
min = -max
|
|
704
|
+
else:
|
|
705
|
+
max = -min
|
|
706
|
+
|
|
707
|
+
if powernorm:
|
|
708
|
+
norm = mpl.colors.PowerNorm(gamma=0.45)
|
|
709
|
+
else:
|
|
710
|
+
if abs(min) > abs(max):
|
|
711
|
+
norm = mpl.colors.Normalize(vmin=min, vmax=abs(min / max) * max)
|
|
712
|
+
else:
|
|
713
|
+
norm = mpl.colors.Normalize(vmin=abs(max / min) * min, vmax=max)
|
|
714
|
+
elif colormap == "grey":
|
|
715
|
+
cmap = "gray"
|
|
716
|
+
elif colormap == "random":
|
|
717
|
+
x = np.random.rand(256 if ncolors is None else ncolors, 3)
|
|
718
|
+
cmap = mpl.colors.LinearSegmentedColormap.from_list("my_colormap", x)
|
|
719
|
+
else:
|
|
720
|
+
cmap = colormap
|
|
721
|
+
|
|
722
|
+
# choose default grey scale map for non-color image
|
|
723
|
+
if cmap is None and len(im.shape) == 2:
|
|
724
|
+
cmap = "gray"
|
|
725
|
+
|
|
726
|
+
# TODO not sure why exclusion for color, nor why float conversion
|
|
727
|
+
if im.ndim == 3 and darken is not None:
|
|
728
|
+
im = float_image(im) / darken
|
|
729
|
+
|
|
730
|
+
if isinstance(cmap, str):
|
|
731
|
+
# cmap = cm.get_cmap(cmap, lut=ncolors)
|
|
732
|
+
cmap = mpl.colormaps[cmap]
|
|
733
|
+
if ncolors is not None:
|
|
734
|
+
cmap = cmap.resampled(ncolors)
|
|
735
|
+
|
|
736
|
+
# handle values outside of range
|
|
737
|
+
#
|
|
738
|
+
# - undercolor, below vmin
|
|
739
|
+
# - overcolor, above vmax
|
|
740
|
+
# - badcolor, nan, -inf, inf
|
|
741
|
+
#
|
|
742
|
+
# only works for greyscale image
|
|
743
|
+
if im.ndim == 2:
|
|
744
|
+
cmap = copy.copy(cmap)
|
|
745
|
+
if undercolor is not None:
|
|
746
|
+
cmap.set_under(color=undercolor)
|
|
747
|
+
if overcolor is not None:
|
|
748
|
+
cmap.set_over(color=overcolor)
|
|
749
|
+
if badcolor is not None:
|
|
750
|
+
cmap.set_bad(color=badcolor)
|
|
751
|
+
# elif im.ndim == 3:
|
|
752
|
+
# if badcolor is not None:
|
|
753
|
+
# cmap.set_bad(color=badcolor)
|
|
754
|
+
|
|
755
|
+
if black != 0:
|
|
756
|
+
if np.issubdtype(im.dtype, np.floating):
|
|
757
|
+
m = 1 - black
|
|
758
|
+
c = black
|
|
759
|
+
im = m * im + c
|
|
760
|
+
norm = mpl.colors.Normalize(0, 1)
|
|
761
|
+
elif np.issubdtype(im.dtype, bool):
|
|
762
|
+
norm = mpl.colors.Normalize(0, 1)
|
|
763
|
+
ncolors = 2
|
|
764
|
+
else:
|
|
765
|
+
max = np.iinfo(im.dtype).max
|
|
766
|
+
black = black * max
|
|
767
|
+
c = black
|
|
768
|
+
m = (max - c) / max
|
|
769
|
+
im = (m * im + c).astype(im.dtype)
|
|
770
|
+
norm = mpl.colors.Normalize(0, max)
|
|
771
|
+
# else:
|
|
772
|
+
# # lift the displayed intensity of black pixels.
|
|
773
|
+
# # set the greyscale mapping [0,M] to [black,1]
|
|
774
|
+
# M = np.max(im)
|
|
775
|
+
# norm = mpl.colors.Normalize(-black * M / (1 - black), M)
|
|
776
|
+
if darken:
|
|
777
|
+
norm = mpl.colors.Normalize(np.min(im), np.max(im) / darken)
|
|
778
|
+
|
|
779
|
+
if gamma:
|
|
780
|
+
cmap.set_gamma(gamma)
|
|
781
|
+
|
|
782
|
+
# print('Colormap is ', cmap)
|
|
783
|
+
|
|
784
|
+
# build up options for imshow
|
|
785
|
+
options = kwargs
|
|
786
|
+
if ynormal:
|
|
787
|
+
options["origin"] = "lower"
|
|
788
|
+
|
|
789
|
+
if extent is not None:
|
|
790
|
+
options["extent"] = extent
|
|
791
|
+
|
|
792
|
+
# display the image
|
|
793
|
+
if len(im.shape) == 3:
|
|
794
|
+
# color image, display in RGB or RGBA order; imshow handles alpha
|
|
795
|
+
# natively for both uint8 (0-255) and float (0.0-1.0) arrays
|
|
796
|
+
target = "RGBA" if "A" in colororder else "RGB"
|
|
797
|
+
idx = [colororder.find(c) for c in target if colororder.find(c) >= 0]
|
|
798
|
+
im_rgb = im[:, :, idx] # reorder planes → RGB or RGBA
|
|
799
|
+
h = ax.imshow(im_rgb, norm=norm, cmap=cmap, **options)
|
|
800
|
+
else:
|
|
801
|
+
# monochrome image, display in indexed mode, where pixel value is mapped through the colormap
|
|
802
|
+
if norm is None:
|
|
803
|
+
# exclude NaN values
|
|
804
|
+
if vrange is None:
|
|
805
|
+
min = np.nanmin(im)
|
|
806
|
+
max = np.nanmax(im)
|
|
807
|
+
else:
|
|
808
|
+
min, max = vrange
|
|
809
|
+
|
|
810
|
+
if colorbar is not False and ncolors is not None:
|
|
811
|
+
# colorbar requested with finite number of colors
|
|
812
|
+
# adjust range so that ticks fall in middle of color segment
|
|
813
|
+
min -= 0.5
|
|
814
|
+
max += 0.5
|
|
815
|
+
norm = mpl.colors.Normalize(vmin=min, vmax=max)
|
|
816
|
+
|
|
817
|
+
h = ax.imshow(im, norm=norm, cmap=cmap, **options)
|
|
818
|
+
|
|
819
|
+
# display the color bar
|
|
820
|
+
if colorbar is not False:
|
|
821
|
+
ticks = None
|
|
822
|
+
labels = None
|
|
823
|
+
cbargs = {}
|
|
824
|
+
if ncolors:
|
|
825
|
+
cbargs["ticks"] = range(ncolors + 1)
|
|
826
|
+
|
|
827
|
+
if isinstance(colorbar, dict):
|
|
828
|
+
# passed options have priority
|
|
829
|
+
if "xticks" in colorbar:
|
|
830
|
+
ticks, labels = colorbar["xticks"]
|
|
831
|
+
del colorbar["xticks"]
|
|
832
|
+
else:
|
|
833
|
+
ticks = None
|
|
834
|
+
labels = None
|
|
835
|
+
cbargs = {**cbargs, **colorbar}
|
|
836
|
+
|
|
837
|
+
cb = fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax, **cbargs)
|
|
838
|
+
if ticks is not None:
|
|
839
|
+
cb.set_ticks(ticks, labels=labels)
|
|
840
|
+
# fig.suptitle(title) # slightly different positioning
|
|
841
|
+
# ax.set_title(title)
|
|
842
|
+
|
|
843
|
+
# hide image axes - by default also removes frame
|
|
844
|
+
# back with ax.spines['top'].set_visible(True) ?
|
|
845
|
+
if not axes:
|
|
846
|
+
ax.axis("off")
|
|
847
|
+
|
|
848
|
+
ax.set_xlabel(axlabels[0])
|
|
849
|
+
ax.set_ylabel(axlabels[1])
|
|
850
|
+
|
|
851
|
+
if grid is not False:
|
|
852
|
+
# if grid is True:
|
|
853
|
+
# ax.grid(True)
|
|
854
|
+
# elif isinstance(grid, str):
|
|
855
|
+
ax.grid(color="y", alpha=0.5, linewidth=0.5)
|
|
856
|
+
|
|
857
|
+
# no frame:
|
|
858
|
+
if not frame:
|
|
859
|
+
# NOTE: for frame tweaking, see matplotlib.spines
|
|
860
|
+
# https://matplotlib.org/3.3.2/api/spines_api.html
|
|
861
|
+
# note: can set spines linewidth:
|
|
862
|
+
# ax.spines['top'].set_linewidth(2.0)
|
|
863
|
+
ax.spines["top"].set_visible(False)
|
|
864
|
+
ax.spines["bottom"].set_visible(False)
|
|
865
|
+
ax.spines["left"].set_visible(False)
|
|
866
|
+
ax.spines["right"].set_visible(False)
|
|
867
|
+
|
|
868
|
+
if savefigname is not None:
|
|
869
|
+
# TODO check valid savefigname
|
|
870
|
+
# set default save file format
|
|
871
|
+
mpl.rcParams["savefig.format"] = "eps"
|
|
872
|
+
plt.draw()
|
|
873
|
+
|
|
874
|
+
# savefig must be called before plt.show
|
|
875
|
+
# after plt.show(), a new fig is automatically created
|
|
876
|
+
plt.savefig(savefigname)
|
|
877
|
+
|
|
878
|
+
# format the pixel value display
|
|
879
|
+
def format_coord(u: float, v: float) -> str:
|
|
880
|
+
u = int(u + 0.5)
|
|
881
|
+
v = int(v + 0.5)
|
|
882
|
+
|
|
883
|
+
try:
|
|
884
|
+
if im.ndim == 2:
|
|
885
|
+
# monochrome image
|
|
886
|
+
x = im[v, u]
|
|
887
|
+
if isinstance(x, np.integer):
|
|
888
|
+
val = f"{x:d}"
|
|
889
|
+
elif isinstance(x, np.floating):
|
|
890
|
+
val = f"{x:.3f}"
|
|
891
|
+
elif isinstance(x, (np.bool_, bool)):
|
|
892
|
+
val = f"{x}"
|
|
813
893
|
else:
|
|
814
|
-
|
|
815
|
-
x = im[v, u, :] # in RGB order
|
|
816
|
-
if colororder == "BGR":
|
|
817
|
-
x = x[::-1]
|
|
818
|
-
if np.issubdtype(x.dtype, np.integer):
|
|
819
|
-
val = [f"{_:d}" for _ in x]
|
|
820
|
-
elif np.issubdtype(x.dtype, np.floating):
|
|
821
|
-
val = [f"{_:.3f}" for _ in x]
|
|
822
|
-
else:
|
|
823
|
-
val = [str(_) for _ in x]
|
|
824
|
-
val = "[" + ", ".join(val) + "]"
|
|
825
|
-
|
|
826
|
-
return f"({u}, {v}): {val} {colororder}, {x.dtype}"
|
|
827
|
-
|
|
828
|
-
except IndexError:
|
|
829
|
-
return ""
|
|
830
|
-
|
|
831
|
-
def key_press(event: Any) -> None:
|
|
832
|
-
if not _pyclip:
|
|
833
|
-
return
|
|
834
|
-
if pyclip is None:
|
|
835
|
-
return
|
|
894
|
+
print(f"unknown pixel type {type(x)}")
|
|
836
895
|
|
|
837
|
-
|
|
838
|
-
u = int(event.xdata + 0.5)
|
|
839
|
-
v = int(event.ydata + 0.5)
|
|
896
|
+
return f"({u}, {v}): {val}"
|
|
840
897
|
else:
|
|
841
|
-
|
|
898
|
+
# color image
|
|
899
|
+
x = im[v, u, :] # in RGB order
|
|
900
|
+
if colororder == "BGR":
|
|
901
|
+
x = x[::-1]
|
|
902
|
+
if np.issubdtype(x.dtype, np.integer):
|
|
903
|
+
val = [f"{_:d}" for _ in x]
|
|
904
|
+
elif np.issubdtype(x.dtype, np.floating):
|
|
905
|
+
val = [f"{_:.3f}" for _ in x]
|
|
906
|
+
else:
|
|
907
|
+
val = [str(_) for _ in x]
|
|
908
|
+
val = "[" + ", ".join(val) + "]"
|
|
842
909
|
|
|
843
|
-
|
|
844
|
-
# print pixel value at mouse click
|
|
845
|
-
pyclip.copy(f"{u},{v}")
|
|
846
|
-
elif event.key == "C":
|
|
847
|
-
# print pixel value at mouse click
|
|
848
|
-
prev = pyclip.paste()
|
|
849
|
-
if isinstance(prev, bytes):
|
|
850
|
-
prev = prev.decode()
|
|
851
|
-
pyclip.copy(f"{prev}\n{u},{v}")
|
|
852
|
-
elif event.key == "x":
|
|
853
|
-
pyclip.copy("")
|
|
854
|
-
elif event.key == "v":
|
|
855
|
-
val = im[v, u, ...]
|
|
856
|
-
pyclip.copy(f"{u},{v},{tuple(val)}")
|
|
857
|
-
elif event.key == "V":
|
|
858
|
-
val = im[v, u, ...]
|
|
859
|
-
prev = pyclip.paste()
|
|
860
|
-
if isinstance(prev, bytes):
|
|
861
|
-
prev = prev.decode()
|
|
862
|
-
pyclip.copy(f"{prev}\n{u},{v},{tuple(val)}")
|
|
863
|
-
|
|
864
|
-
fig.canvas.mpl_connect("key_press_event", key_press)
|
|
865
|
-
|
|
866
|
-
if coordformat is None:
|
|
867
|
-
ax.format_coord = format_coord
|
|
868
|
-
else:
|
|
869
|
-
ax.format_coord = coordformat
|
|
910
|
+
return f"({u}, {v}): {val} {colororder}, {x.dtype}"
|
|
870
911
|
|
|
871
|
-
|
|
872
|
-
|
|
912
|
+
except IndexError:
|
|
913
|
+
return ""
|
|
873
914
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
915
|
+
def key_press(event: Any) -> None:
|
|
916
|
+
if not _pyclip:
|
|
917
|
+
return
|
|
918
|
+
if pyclip is None:
|
|
919
|
+
return
|
|
877
920
|
|
|
878
|
-
|
|
921
|
+
if event.inaxes is not None:
|
|
922
|
+
u = int(event.xdata + 0.5)
|
|
923
|
+
v = int(event.ydata + 0.5)
|
|
924
|
+
else:
|
|
925
|
+
return
|
|
879
926
|
|
|
880
|
-
|
|
927
|
+
if event.key == "c":
|
|
928
|
+
# print pixel value at mouse click
|
|
929
|
+
pyclip.copy(f"{u},{v}")
|
|
930
|
+
elif event.key == "C":
|
|
931
|
+
# print pixel value at mouse click
|
|
932
|
+
prev = pyclip.paste()
|
|
933
|
+
if isinstance(prev, bytes):
|
|
934
|
+
prev = prev.decode()
|
|
935
|
+
pyclip.copy(f"{prev}\n{u},{v}")
|
|
936
|
+
elif event.key == "x":
|
|
937
|
+
pyclip.copy("")
|
|
938
|
+
elif event.key == "v":
|
|
939
|
+
val = im[v, u, ...]
|
|
940
|
+
pyclip.copy(f"{u},{v},{tuple(val)}")
|
|
941
|
+
elif event.key == "V":
|
|
942
|
+
val = im[v, u, ...]
|
|
943
|
+
prev = pyclip.paste()
|
|
944
|
+
if isinstance(prev, bytes):
|
|
945
|
+
prev = prev.decode()
|
|
946
|
+
pyclip.copy(f"{prev}\n{u},{v},{tuple(val)}")
|
|
947
|
+
|
|
948
|
+
fig.canvas.mpl_connect("key_press_event", key_press)
|
|
949
|
+
|
|
950
|
+
if coordformat is None:
|
|
951
|
+
ax.format_coord = format_coord
|
|
881
952
|
else:
|
|
882
|
-
|
|
883
|
-
|
|
953
|
+
ax.format_coord = coordformat
|
|
954
|
+
|
|
955
|
+
# don't display data
|
|
956
|
+
h.format_cursor_data = lambda data: ""
|
|
957
|
+
|
|
958
|
+
if fps is not None:
|
|
959
|
+
# print("pausing", 1.0 / fps)
|
|
960
|
+
plt.pause(1.0 / fps)
|
|
961
|
+
|
|
962
|
+
safe_plt_show(block=block)
|
|
963
|
+
|
|
964
|
+
return h
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
def _idisp_opencv(
|
|
968
|
+
im: np.ndarray,
|
|
969
|
+
colororder: str = "RGB",
|
|
970
|
+
title: str | None = None,
|
|
971
|
+
block: bool | float | None = None,
|
|
972
|
+
fps: float | None = None,
|
|
973
|
+
reuse: bool = False,
|
|
974
|
+
) -> None:
|
|
975
|
+
"""OpenCV (HighGUI) backend for :func:`idisp`.
|
|
884
976
|
|
|
885
|
-
|
|
977
|
+
Not part of the public API -- called from :func:`idisp` when
|
|
978
|
+
``matplotlib=False``. Split out so each display backend's logic can be
|
|
979
|
+
read, tested and fixed independently of the other; see :func:`idisp`
|
|
980
|
+
for the full parameter documentation.
|
|
981
|
+
"""
|
|
982
|
+
## display using OpenCV
|
|
983
|
+
global __last_window_number
|
|
984
|
+
|
|
985
|
+
if title is None:
|
|
986
|
+
if reuse:
|
|
987
|
+
# fixed name (no counter) so repeated calls in an
|
|
988
|
+
# animation loop share the same window
|
|
989
|
+
title = "idisp"
|
|
990
|
+
else:
|
|
886
991
|
# create a unique window name for each call
|
|
887
992
|
title = "idisp." + str(__last_window_number)
|
|
888
993
|
__last_window_number += 1
|
|
889
994
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
995
|
+
# At this point title is guaranteed to be a string
|
|
996
|
+
assert title is not None
|
|
997
|
+
cv2.namedWindow(title, cv2.WINDOW_AUTOSIZE)
|
|
998
|
+
|
|
999
|
+
if im.ndim == 3:
|
|
1000
|
+
# color image, display in BGR order, but allow user to specify color order of input image
|
|
1001
|
+
idx = []
|
|
1002
|
+
for c in "BGR":
|
|
1003
|
+
j = colororder.find(c)
|
|
1004
|
+
if j >= 0:
|
|
1005
|
+
idx.append(j)
|
|
1006
|
+
cv2.imshow(title, im[:, :, idx]) # convert → BGR order and display
|
|
1007
|
+
else:
|
|
1008
|
+
# monochrome image, display in indexed mode, where pixel value is mapped through the colormap
|
|
1009
|
+
cv2.imshow(title, im) # make sure BGR format image
|
|
905
1010
|
|
|
906
|
-
|
|
1011
|
+
cv2.waitKey(1)
|
|
907
1012
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1013
|
+
if fps is not None:
|
|
1014
|
+
# wait one frame time
|
|
1015
|
+
cv2.waitKey(round(1000.0 / fps))
|
|
911
1016
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
1017
|
+
if block is True:
|
|
1018
|
+
while True:
|
|
1019
|
+
k = cv2.waitKey(delay=0) # wait forever for keystroke
|
|
1020
|
+
if k == ord("q"):
|
|
1021
|
+
assert title is not None
|
|
1022
|
+
cv2.destroyWindow(title)
|
|
1023
|
+
cv2.waitKey(1)
|
|
1024
|
+
break
|
|
920
1025
|
|
|
921
|
-
|
|
922
|
-
|
|
1026
|
+
# TODO fig, ax equivalent for OpenCV? how to print/plot to the same
|
|
1027
|
+
# window/set of axes?
|
|
923
1028
|
|
|
924
1029
|
|
|
925
1030
|
def set_window_title(title: str) -> None:
|
|
@@ -1176,7 +1281,7 @@ def convert(
|
|
|
1176
1281
|
:type grey: bool or 'ITU601' [default] or 'ITU709'
|
|
1177
1282
|
:param gray: synonym for ``grey``
|
|
1178
1283
|
:param dtype: a NumPy dtype string such as ``"uint8"``, ``"int16"``, ``"float32"`` or
|
|
1179
|
-
a NumPy type like ``np.uint8``.
|
|
1284
|
+
a NumPy type like ``np.uint8``. |dtype_aliases|
|
|
1180
1285
|
:type dtype: str
|
|
1181
1286
|
:param rgb: force color image to RGB order, otherwise BGR
|
|
1182
1287
|
:type rgb: bool, optional
|
|
@@ -1250,17 +1355,10 @@ def convert(
|
|
|
1250
1355
|
if mono and len(image.shape) == 3:
|
|
1251
1356
|
image = colorspace_convert(image, colororder, "grey")
|
|
1252
1357
|
|
|
1253
|
-
dtype_alias = {
|
|
1254
|
-
"int": "uint8",
|
|
1255
|
-
"float": "float32",
|
|
1256
|
-
"double": "float64",
|
|
1257
|
-
"half": "float16",
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
1358
|
if dtype is not None:
|
|
1261
1359
|
# default types
|
|
1262
1360
|
if isinstance(dtype, str):
|
|
1263
|
-
dtype =
|
|
1361
|
+
dtype = DTYPE_ALIASES.get(dtype, dtype)
|
|
1264
1362
|
|
|
1265
1363
|
if "int" in str(dtype):
|
|
1266
1364
|
image = int_image(image, intclass=dtype, maxintval=maxintval)
|