showtens 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.
showtens/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .util import gridify
2
+ from .images import showImage, saveImage
3
+ from .videos import saveVideo
showtens/images.py ADDED
@@ -0,0 +1,115 @@
1
+ import os
2
+ import matplotlib.pyplot as plt
3
+ from .imports import import_torch, import_torchvision
4
+ from .util import gridify, _create_folder
5
+
6
+ torch = import_torch()
7
+ torchvision = import_torchvision()
8
+
9
+
10
+ @torch.no_grad()
11
+ def showImage(tensor, columns=None, colorbar=False, max_width=None, padding=3, pad_value=0.0):
12
+ """ "
13
+ Shows tensor as an image using pyplot.
14
+ Any extra dimensions (*,C,H,W) are treated as batch dimensions.
15
+
16
+ Args:
17
+ tensor : (H,W) or (C,H,W) or (*,C,H,W) tensor to display
18
+ columns : number of columns to use for the grid of images (default 8 or less)
19
+ colorbar : whether to add a colorbar to the image, only works for grayscale images (default False)
20
+ max_width : maximum width of the image
21
+ padding : number of pixels between images in the grid
22
+ pad_value : inter-padding value for the grid of images
23
+ """
24
+ tensor = _format_image(
25
+ tensor, columns=columns, max_width=max_width, padding=padding, pad_value=pad_value
26
+ ) # (C,H',W') ready to show
27
+
28
+ plt.imshow(tensor.permute((1, 2, 0)))
29
+ plt.axis("off")
30
+ if tensor.shape[0] == 1 and colorbar:
31
+ plt.colorbar()
32
+ plt.show()
33
+
34
+
35
+ @torch.no_grad()
36
+ def saveImage(
37
+ tensor,
38
+ folder,
39
+ name="imagetensor",
40
+ columns=None,
41
+ colorbar=False,
42
+ max_width=None,
43
+ padding=3,
44
+ pad_value=0.0,
45
+ create_folder=True,
46
+ ):
47
+ """
48
+ Saves tensor as a png image using pyplot.
49
+ Any extra dimensions (*,C,H,W) are treated as batch dimensions.
50
+
51
+ Args:
52
+ tensor : (H,W) or (C,H,W) or (*,C,H,W) tensor to display
53
+ folder : relative path of folder where to save the image
54
+ name : name of the image (do not include extension)
55
+ columns : number of columns to use for the grid of images (default 8 or less)
56
+ colorbar : whether to add a colorbar to the image, only works for grayscale images (default False)
57
+ max_width : maximum width of the image
58
+ padding : number of pixels between images in the grid
59
+ pad_value : inter-padding value for the grid of images
60
+ create_folder : whether to create the folder if it does not exist (default True)
61
+ """
62
+ _create_folder(folder, create_folder)
63
+
64
+ tensor = _format_image(
65
+ tensor, columns=columns, max_width=max_width, padding=padding, pad_value=pad_value
66
+ ) # (C,H',W') ready to save
67
+ plt.imshow(tensor.permute((1, 2, 0)))
68
+ plt.axis("off")
69
+ if tensor.shape[0] == 1 and colorbar:
70
+ plt.colorbar()
71
+
72
+ plt.savefig(os.path.join(folder, f"{name}.png"), bbox_inches="tight", pad_inches=0)
73
+
74
+
75
+ @torch.no_grad()
76
+ def _format_image(tensor, columns=None, max_width=None, padding=3, pad_value=0.0):
77
+ """ "
78
+ Shows tensor as an image using pyplot.
79
+ Any extra dimensions (*,C,H,W) are treated as batch dimensions.
80
+
81
+ Args:
82
+ tensor : (H,W) or (C,H,W) or (*,C,H,W) tensor to display
83
+ columns : number of columns to use for the grid of images (default 8 or less)
84
+ max_width : maximum width of the image
85
+ padding : number of pixels between images in the grid
86
+ pad_value : inter-padding value for the grid of images
87
+ """
88
+ tensor = tensor.detach().cpu()
89
+
90
+ extra_params = dict(columns=columns, max_width=max_width, pad_value=pad_value, padding=padding)
91
+ if len(tensor.shape) == 2:
92
+ # Add batch and channel dimensions
93
+ return _format_image(tensor[None, :, :], **extra_params)
94
+ elif len(tensor.shape) == 3:
95
+ # Reached (C,H,W)
96
+ return tensor
97
+ elif len(tensor.shape) == 4:
98
+ # Gridify assuming (B,C,H,W)
99
+ B = tensor.shape[0]
100
+ if columns is not None:
101
+ numCol = columns
102
+ else:
103
+ numCol = min(8, B)
104
+ tensor = gridify(tensor, columns=numCol, max_width=max_width, pad_value=pad_value, padding=padding)
105
+
106
+ return _format_image(tensor, **extra_params)
107
+ elif len(tensor.shape) > 4:
108
+ # Collapse extra dimension to batch
109
+ tensor = tensor.reshape(
110
+ (-1, tensor.shape[-3], tensor.shape[-2], tensor.shape[-1])
111
+ ) # assume all batch dimensions
112
+ print("Assuming extra dimension are all batch dimensions, newshape : ", tensor.shape)
113
+ return _format_image(tensor, **extra_params)
114
+ else:
115
+ raise Exception(f"Tensor shape should be (H,W), (C,H,W) or (*,C,H,W), but got : {tensor.shape} !")
showtens/imports.py ADDED
@@ -0,0 +1,35 @@
1
+ def import_torch():
2
+ """
3
+ Lazily import torch, giving helpful error message if not installed.
4
+ """
5
+ try:
6
+ import torch
7
+
8
+ return torch
9
+ except ImportError:
10
+ raise ImportError(
11
+ "PyTorch is not installed. ShowTens requires PyTorch. "
12
+ "Please install it with one of the following commands:\n\n"
13
+ "- For CPU only: pip install torch\n"
14
+ "- For CUDA support: Visit https://pytorch.org for installation instructions "
15
+ "specific to your system and CUDA version."
16
+ )
17
+
18
+
19
+ def import_torchvision():
20
+ """
21
+ Lazily import torchvision.transforms, giving helpful error message if not installed.
22
+ """
23
+ try:
24
+ import torchvision
25
+
26
+ return torchvision
27
+ except ImportError:
28
+ raise ImportError(
29
+ "torchvision is not installed. ShowTens requires torchvision. "
30
+ "Please install it with the following command:\n\n"
31
+ "pip install torchvision"
32
+ )
33
+
34
+
35
+ # torch = import_torch()
showtens/util.py ADDED
@@ -0,0 +1,72 @@
1
+ from .imports import import_torch, import_torchvision
2
+ import os
3
+
4
+ torch = import_torch()
5
+
6
+
7
+ @torch.no_grad()
8
+ def gridify(tensor: torch.Tensor, max_width=None, columns=None, padding=3, pad_value=0.0):
9
+ """
10
+ Makes a grid of images/videos from a batch of images.
11
+ Like torchvision's make_grid, but more flexible.
12
+ Accepts (B,*,H,W)
13
+
14
+ Args:
15
+ tensor : (B,*,H,W) tensor
16
+ max_width : max width of the output grid. Resizes images to fit the width
17
+ columns : number of columns of the grid. If None, uses 8 or less
18
+ padding : padding to add to the images
19
+ pad_value : color of the padding
20
+
21
+ Returns:
22
+ (*,H',W') tensor, representing the grid of images/videos
23
+ """
24
+ transf = import_torchvision().transforms
25
+
26
+ B, H, W = tensor.shape[0], tensor.shape[-2], tensor.shape[-1]
27
+ device = tensor.device
28
+ if columns is not None:
29
+ numCol = columns
30
+ else:
31
+ numCol = min(8, B)
32
+
33
+ black_cols = (-B) % numCol
34
+ tensor = torch.cat(
35
+ [tensor, torch.zeros(black_cols, *tensor.shape[1:], device=device)], dim=0
36
+ ) # (B',*,H,W)
37
+ tensor = transf.Pad(padding, fill=pad_value)(tensor) # (B',*,H+padding*2,W+padding*2)
38
+
39
+ B, H, W = tensor.shape[0], tensor.shape[-2], tensor.shape[-1]
40
+ rest_dim = tensor.shape[1:-2]
41
+
42
+ rest_dim_prod = 1
43
+ for dim in rest_dim:
44
+ rest_dim_prod *= dim
45
+
46
+ if max_width is not None:
47
+ resize_ratio = max_width / (W * numCol)
48
+ if resize_ratio < 1:
49
+ indiv_tens_size = int(H * resize_ratio), int(W * resize_ratio)
50
+ tensor = tensor.reshape((B, rest_dim_prod, H, W))
51
+ tensor = transf.Resize(indiv_tens_size, antialias=True)(tensor) # (B',rest_dim_prod,H',W')
52
+
53
+ B, H, W = tensor.shape[0], tensor.shape[-2], tensor.shape[-1]
54
+ assert B % numCol == 0
55
+
56
+ numRows = B // numCol
57
+
58
+ tensor = tensor.reshape((numRows, numCol, rest_dim_prod, H, W)) # (numRows,numCol,rest_dim_prod,H',W')
59
+ tensor = torch.einsum("nmrhw->rnhmw", tensor) # (rest_prod,numRows,H',numCol,W')
60
+ tensor = tensor.reshape((rest_dim_prod, numRows * H, numCol * W)) # (rest_prod,numRows*H,numCol*W)
61
+ tensor = tensor.reshape((*rest_dim, numRows * H, numCol * W)) # (*,numRows*H,numCol*W)
62
+
63
+ return tensor
64
+
65
+
66
+ @torch.no_grad()
67
+ def _create_folder(folder: str, create_folder: bool = True):
68
+ if create_folder:
69
+ os.makedirs(folder, exist_ok=True)
70
+ else:
71
+ if not (os.path.exists(folder)):
72
+ raise FileNotFoundError(f"Folder {folder} does not exist !")
showtens/videos.py ADDED
@@ -0,0 +1,104 @@
1
+ import os
2
+ from .util import gridify, import_torch, _create_folder
3
+ import cv2, numpy as np
4
+
5
+ torch = import_torch()
6
+
7
+
8
+ @torch.no_grad()
9
+ def showVideo(tensor, fps=30, columns=None, max_width=None, padding=3, pad_value=0.0):
10
+ """
11
+ Shows tensor as a video. Accepts both (T,H,W), (T,3,H,W) and (*,T,3,H,W) float tensors.
12
+
13
+ Args:
14
+ tensor : (T,H,W) or (T,3,H,W) or (*,T,3,H,W) float tensor
15
+ columns : number of columns to use for the grid of videos (default 8 or less)
16
+ fps : fps of the video (default 30)
17
+ out_size : Height of output video (height adapts to not deform videos) (default 800)
18
+ """
19
+ return NotImplementedError("showVideo not implemented yet, use saveVideo instead")
20
+
21
+
22
+ @torch.no_grad()
23
+ def saveVideo(
24
+ tensor,
25
+ folder,
26
+ name="videotensor",
27
+ fps=30,
28
+ columns=None,
29
+ max_width=None,
30
+ padding=3,
31
+ pad_value=0.0,
32
+ create_folder=True,
33
+ ):
34
+ """
35
+ Saves tensor as a video. Accepts both (T,H,W), (T,3,H,W) and (*,T,3,H,W) float tensors.
36
+ Assumes that the tensor value are in [0,1], clips them otherwise.
37
+
38
+ Args:
39
+ tensor : (T,H,W) or (T,3,H,W) or (*,T,3,H,W) float tensor
40
+ folder : path to save the video
41
+ name : name of the video
42
+ fps : fps of the video (default 30)
43
+ columns : number of columns to use for the grid of videos (default 8 or less)
44
+ max_width : maximum width of the image
45
+ padding : number of pixels between images in the grid
46
+ pad_value : inter-padding value for the grid of images
47
+ """
48
+ _create_folder(folder, create_folder)
49
+
50
+ tensor = _format_video(
51
+ tensor, columns=columns, fps=fps, max_width=max_width, padding=padding, pad_value=pad_value
52
+ ) # (T,3,H',W') ready to save
53
+ T, C, H, W = tensor.shape
54
+ output_file = os.path.join(folder, f"{name}.mp4")
55
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
56
+ video = cv2.VideoWriter(output_file, fourcc, fps, (W, H))
57
+
58
+ to_save = (255 * tensor.permute(0, 2, 3, 1).cpu().numpy()).astype(np.uint8)
59
+
60
+ for t in range(T):
61
+ frame = to_save[t]
62
+ frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
63
+ video.write(frame)
64
+
65
+ video.release()
66
+
67
+
68
+ def _format_video(tensor, columns=None, fps=30, max_width=None, padding=3, pad_value=0.0):
69
+ """
70
+ Formats tensor as a video. Accepts both (T,H,W), (T,3,H,W) and (*,T,3,H,W) float tensors.
71
+ Assumes that the tensor value are in [0,1], clips them otherwise.
72
+
73
+ Args:
74
+ tensor : (T,H,W) or (T,3,H,W) or (*,T,3,H,W) float tensor
75
+ columns : number of columns to use for the grid of videos (default 8 or less)
76
+ fps : fps of the video (default 30)
77
+ max_width : maximum width of the image
78
+ padding : number of pixels between images in the grid
79
+ pad_value : inter-padding value for the grid of images
80
+ """
81
+ tensor = tensor.detach().cpu()
82
+ extra_params = dict(columns=columns, fps=fps, max_width=max_width, pad_value=pad_value, padding=padding)
83
+
84
+ if len(tensor.shape) == 3:
85
+ # add channel dimension
86
+ tensor = tensor[:, None, :, :].expand(-1, 3, -1, -1) # (T,3,H,W)
87
+ return _format_video(tensor, **extra_params)
88
+ elif len(tensor.shape) == 4:
89
+ if tensor.shape[1] == 1:
90
+ print("Assuming gray-scale video")
91
+ tensor = tensor.expand(-1, 3, -1, -1) # (T,3,H,W)
92
+ assert tensor.shape[1] == 3, f"Tensor shape should be (T,H,W), (T,3,H,W) or (*,T,3,H,W) !"
93
+ # A single video
94
+ return tensor
95
+ elif len(tensor.shape) == 5:
96
+ tensor = gridify(tensor, max_width=max_width, columns=columns, padding=padding, pad_value=pad_value)
97
+ return _format_video(tensor, **extra_params)
98
+ elif len(tensor.shape) > 5:
99
+ tensor = tensor.reshape((-1, *tensor.shape[-4:]))
100
+ return _format_video(tensor, **extra_params)
101
+ else:
102
+ raise ValueError(
103
+ f"Tensor shape should be (T,H,W), (T,3,H,W) or (*,T,3,H,W), but got : {tensor.shape} !"
104
+ )
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.1
2
+ Name: showtens
3
+ Version: 0.0
4
+ Summary: Visualize torch tensors EASILY.
5
+ Author-email: Vassilis Papadopoulos <vassilis.physics@gmail.com>
6
+ License: MIT License
7
+ Project-URL: Homepage, https://github.com/frotaur/showtens
8
+ Project-URL: Bug Tracker, https://github.com/frotaur/showtens/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: matplotlib
16
+ Requires-Dist: opencv-python
17
+ Requires-Dist: numpy
18
+
19
+ # ShowTens : visualize torch tensors EASILY
20
+
21
+ ShowTens is a simple pytorch package that allows painless and flexible visualizations of image and video tensors.
22
+
23
+ \<ADD VISUALIZATION VIDEO HERE\>
24
+
25
+ ## Installation
26
+
27
+ `pip install showtens`
28
+
29
+ Make sure `torch`and `torchvision` are installed, as the package depends on them.
30
+
31
+ ## Usage
32
+ ```python
33
+ import torch
34
+ from showTens import showImage
35
+
36
+ image1 = torch.rand((3,100,100)) # (C,H,W) image
37
+ showImage(image1) # Displays the image using matplotlib
38
+ image2 = torch.rand((4,4,3,100,100)) # (B1,B2,C,H,W), two batch dimensions
39
+ showImage(image2,colums=4) # Will display as a 4*4 grid
40
+
41
+ from showTens import saveImage
42
+ saveImage(tensor=image1,folder='saved_images',name='imagetensor')
43
+
44
+ from showTens import saveVideo
45
+ video1 = torch.rand((60,3,200,200))
46
+ saveVideo(tensor=video1,folder='save_videos',name='videotensor',fps=30)
47
+ video2 = torch.rand((4,3,200,200)) # (B,T,C,H,W), batch of videos
48
+ saveVideo(tensor=video2,folder='save_videos',name='videobatch',fps=30,columns=2) # 2*2 video grid
49
+ ```
@@ -0,0 +1,9 @@
1
+ showtens/__init__.py,sha256=r0JRXhXHlVJmITQlPQINI0rtM7vf_Mn9EELcU_Wh4g8,98
2
+ showtens/images.py,sha256=-9RGBhpBXmXjV8d5c-M8df4mUddoZgivbaaCIwf01_A,4380
3
+ showtens/imports.py,sha256=xx9G4Ap3uTAlXxh7LaNvIz-P0rSMsiMxeGQc5oTo3Dw,1056
4
+ showtens/util.py,sha256=g_2B-KV_OSTakn9P6Ku2u6FTkxhbZfhywYpgnch4fgA,2592
5
+ showtens/videos.py,sha256=Gpvisiqq_7w0a9RhkXJOXEf9qdPW3ROTwrV-UYJDyIE,3995
6
+ showtens-0.0.dist-info/METADATA,sha256=6bGZPusjOFlZcMCq77U1bb1Y-Gdy8iicehfUbQLCo7E,1708
7
+ showtens-0.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
8
+ showtens-0.0.dist-info/top_level.txt,sha256=1SACMFm7OZu9Dl-DE-HZyHym7Lan02cXwMWAMWj5zsA,9
9
+ showtens-0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.3.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ showtens