dragon-ml-toolbox 2.3.0__py3-none-any.whl → 3.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.
@@ -1,231 +0,0 @@
1
- import os
2
- import imghdr
3
- from PIL import Image, ImageOps
4
- from typing import Literal
5
- from torchvision import transforms
6
- import torch
7
- from .utilities import _script_info
8
-
9
-
10
- __all__ = [
11
- "inspect_images",
12
- "image_augmentation",
13
- "ResizeAspectFill",
14
- "is_image",
15
- "model_predict"
16
- ]
17
-
18
-
19
- def inspect_images(path: str):
20
- """
21
- Prints out the types, sizes and channels of image files found in the directory and its subdirectories.
22
-
23
- Possible band names (channels):
24
- * “R”: Red channel
25
- * “G”: Green channel
26
- * “B”: Blue channel
27
- * “A”: Alpha (transparency) channel
28
- * “L”: Luminance (grayscale) channel
29
- * “P”: Palette channel
30
- * “I”: Integer channel
31
- * “F”: Floating point channel
32
-
33
- Args:
34
- path (string): path to target directory.
35
- """
36
- # Non-image files present?
37
- red_flag = False
38
- non_image = set()
39
- # Image types found
40
- img_types = set()
41
- # Image sizes found
42
- img_sizes = set()
43
- # Color channels found
44
- img_channels = set()
45
- # Number of images
46
- img_counter = 0
47
- # Loop through files in the directory and subdirectories
48
- for root, directories, files in os.walk(path):
49
- for filename in files:
50
- filepath = os.path.join(root, filename)
51
- img_type = imghdr.what(filepath)
52
- # Not an image file
53
- if img_type is None:
54
- red_flag = True
55
- non_image.add(filename)
56
- continue
57
- # Image type
58
- img_types.add(img_type)
59
- # Image counter
60
- img_counter += 1
61
- # Image size
62
- img = Image.open(filepath)
63
- img_sizes.add(img.size)
64
- # Image color channels
65
- channels = img.getbands()
66
- for code in channels:
67
- img_channels.add(code)
68
-
69
- if red_flag:
70
- print(f"⚠️ Non-image files found: {non_image}")
71
- # Print results
72
- print(f"Image types found: {img_types}\nImage sizes found: {img_sizes}\nImage channels found: {img_channels}\nImages found: {img_counter}")
73
-
74
-
75
- def image_augmentation(path: str, samples: int=100, size: int=256, mode: Literal["RGB", "L"]="RGB", jitter_ratio: float=0.0,
76
- rotation_deg=270, output: Literal["jpeg", "png", "tiff", "bmp"]="jpeg"):
77
- """
78
- Perform image augmentation on a directory containing image files.
79
- A new directory "temp_augmented_images" will be created; an error will be raised if it already exists.
80
-
81
- Args:
82
- path (str): Path to target directory.
83
- samples (int, optional): Number of images to create per image in the directory. Defaults to 100.
84
- size (int, optional): Image size to resize to. Defaults to 256.
85
- mode (str, optional): 'RGB' for 3 channels, 'L' for 1 grayscale channel.
86
- jitter_ratio (float, optional): Brightness and Contrast factor to use in the ColorJitter transform. Defaults to 0.
87
- rotation_deg (int, optional): Range for the rotation transformation. Defaults to 270.
88
- output (str, optional): output image format. Defaults to 'jpeg'.
89
- """
90
- # Define the transformations
91
- transform = transforms.Compose([
92
- transforms.Resize(size=(int(size*1.2),int(size*1.2))),
93
- transforms.CenterCrop(size=size),
94
- transforms.ColorJitter(brightness=jitter_ratio, contrast=jitter_ratio),
95
- transforms.RandomHorizontalFlip(p=0.5),
96
- transforms.RandomRotation(degrees=rotation_deg),
97
- ])
98
-
99
- # Create container folder
100
- dir_name = "temp_augmented_images"
101
- os.makedirs(dir_name, exist_ok=False)
102
-
103
- # Keep track of non-image files
104
- non_image = set()
105
-
106
- # Apply transformation to each image in path
107
- for filename in os.listdir(path):
108
- filepath = os.path.join(path, filename)
109
-
110
- # Is image file?
111
- if not is_image(filename):
112
- non_image.add(filename)
113
- continue
114
- # if imghdr.what(filepath) is None:
115
- # non_image.add(filename)
116
- # continue
117
-
118
- # current image
119
- img = Image.open(filepath)
120
-
121
- # Convert to RGB or grayscale
122
- if mode == "RGB":
123
- img = img.convert("RGB")
124
- else:
125
- img = img.convert("L")
126
-
127
- # Create and save images
128
- for i in range(1, samples+1):
129
- new_img = transform(img)
130
- filename_no_ext = os.path.splitext(filename)[0]
131
- new_img.save(f"{dir_name}/{filename_no_ext}_{i}.{output}")
132
-
133
- # Print non-image files
134
- if len(non_image) != 0:
135
- print(f"Files not processed: {non_image}")
136
-
137
-
138
- class ResizeAspectFill:
139
- """
140
- Custom transformation to make a square image (width/height = 1).
141
-
142
- Implemented by padding with a `pad_color` border an image of size (w, h) when w > h or w < h to match the longest side.
143
- """
144
- def __init__(self, pad_color: Literal["black", "white"]="black") -> None:
145
- self.pad_color = pad_color
146
-
147
- def __call__(self, image: Image.Image):
148
- # Check correct PIL.Image file
149
- if not isinstance(image, Image.Image):
150
- raise TypeError(f"Expected PIL.Image.Image, got {type(image).__name__}")
151
-
152
- w = image.width
153
- h = image.height
154
- delta = abs(w - h)
155
-
156
- if w > h:
157
- # padding: left, top, right, bottom
158
- padding = (0, 0, 0, delta)
159
- elif h > w:
160
- padding = (0, 0, delta, 0)
161
- else:
162
- padding = (0, 0)
163
-
164
- return ImageOps.expand(image=image, border=padding, fill=self.pad_color)
165
-
166
-
167
- def is_image(file: str):
168
- """
169
- Returns `True` if the file is an image, `False` otherwise.
170
-
171
- Args:
172
- `file`, filename with extension.
173
- """
174
- try:
175
- Image.open(file)
176
- except IOError:
177
- return False
178
- else:
179
- return True
180
-
181
-
182
- def model_predict(model: torch.nn.Module, kind: Literal["regression", "classification"], samples_list: list[torch.Tensor],
183
- device: Literal["cpu", "cuda", "mps"]='cpu', view_as: tuple[int,int]=(1,-1), add_batch_dimension: bool=True):
184
- """
185
- Returns a list containing lists of predicted values, one for each input sample.
186
-
187
- Each sample must be a tensor and have the same shape and normalization expected by the model.
188
-
189
- Args:
190
- `model`: A trained PyTorch model.
191
-
192
- `kind`: Regression or Classification task.
193
-
194
- `samples_list`: A list of input tensors.
195
-
196
- `device`: Device to use, default is CPU.
197
-
198
- `view_as`: Reshape each model output, default is (1,-1).
199
-
200
- `add_batch_dimension`: Automatically adds the batch dimension to each sample shape.
201
- """
202
- # Validate device
203
- if device == "cuda":
204
- if not torch.cuda.is_available():
205
- print("CUDA not available, switching to CPU.")
206
- device = "cpu"
207
- elif device == "mps":
208
- if not torch.backends.mps.is_available():
209
- print("MPS not available, switching to CPU.")
210
- device = "cpu"
211
-
212
- model.eval()
213
- results = list()
214
- with torch.no_grad():
215
- for data_point in samples_list:
216
- if add_batch_dimension:
217
- data_point = data_point.unsqueeze(0).to(device)
218
- else:
219
- data_point = data_point.to(device)
220
-
221
- output = model(data_point)
222
- if kind == "classification":
223
- results.append(output.argmax(dim=1).view(view_as).cpu().tolist())
224
- else: #regression
225
- results.append(output.view(view_as).cpu().tolist())
226
-
227
- return results
228
-
229
-
230
- def info():
231
- _script_info(__all__)
File without changes