current status
|
|
@ -0,0 +1,50 @@
|
||||||
|
import torch.utils.data
|
||||||
|
import numpy as np
|
||||||
|
import numpy.random as npr
|
||||||
|
import torchvision
|
||||||
|
|
||||||
|
|
||||||
|
class AImagesDataset(torch.utils.data.Dataset):
|
||||||
|
@staticmethod
|
||||||
|
def augment_tensor(t: torch.Tensor, i: int) -> (torch.Tensor, str):
|
||||||
|
match i % 7:
|
||||||
|
case 0:
|
||||||
|
return t, "Original"
|
||||||
|
case 1:
|
||||||
|
return torchvision.transforms.GaussianBlur(kernel_size=5).forward(t), "GaussianBlur"
|
||||||
|
case 2:
|
||||||
|
return torchvision.transforms.RandomRotation(degrees=180).forward(t), "RandomRotation"
|
||||||
|
case 3:
|
||||||
|
return torchvision.transforms.RandomVerticalFlip().forward(t), "RandomVerticalFlip"
|
||||||
|
case 4:
|
||||||
|
return torchvision.transforms.RandomHorizontalFlip().forward(t), "RandomHorizontalFlip"
|
||||||
|
case 5:
|
||||||
|
return torchvision.transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2,
|
||||||
|
hue=0.1).forward(t), "ColorJitter"
|
||||||
|
case 6:
|
||||||
|
rng = npr.default_rng()
|
||||||
|
return AImagesDataset.augment_tensor(
|
||||||
|
AImagesDataset.augment_tensor(
|
||||||
|
AImagesDataset.augment_tensor(t, rng.integers(1, 6))[0],
|
||||||
|
rng.integers(1, 6))[0],
|
||||||
|
rng.integers(1, 6))[0], "Compose"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def augment_image(
|
||||||
|
img_np: np.ndarray,
|
||||||
|
index: int
|
||||||
|
) -> (torch.Tensor, str):
|
||||||
|
tensor = torch.from_numpy(img_np)
|
||||||
|
return AImagesDataset.augment_tensor(tensor, index)
|
||||||
|
|
||||||
|
def __init__(self, data_set: torch.utils.data.Dataset):
|
||||||
|
super().__init__()
|
||||||
|
self.data = data_set
|
||||||
|
|
||||||
|
def __getitem__(self, index: int):
|
||||||
|
image, class_id, class_name, image_filepath = self.data[index // 7]
|
||||||
|
img, transform = AImagesDataset.augment_tensor(image, index)
|
||||||
|
return img, transform, index, class_id, class_name, image_filepath
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.data.__len__() * 7
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
import queue
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import tkinter as tk
|
||||||
|
import tkinter.ttk as ttk
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class ClearableQueue(queue.Queue):
|
||||||
|
def clear(self):
|
||||||
|
with self.mutex:
|
||||||
|
while self._qsize() != 0:
|
||||||
|
_ = self._get()
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def acquire_timeout(lock, timeout):
|
||||||
|
result = lock.acquire(timeout=timeout)
|
||||||
|
try:
|
||||||
|
yield result
|
||||||
|
finally:
|
||||||
|
if result:
|
||||||
|
lock.release()
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncProgress(ttk.Frame):
|
||||||
|
@dataclass(repr=False)
|
||||||
|
class Stats:
|
||||||
|
total: int
|
||||||
|
current: int = 0
|
||||||
|
start_time: float = time.time()
|
||||||
|
last_elapsed_time: float = 0
|
||||||
|
total_elapsed_time: float = 0
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
ips = 1 / self.last_elapsed_time if self.last_elapsed_time != 0 else 0
|
||||||
|
return (f"{'{v:{p}d}'.format(v=self.current, p=len(str(self.total)))}/{self.total} "
|
||||||
|
f"| {ips:.2f}it/s "
|
||||||
|
f"| elapsed: {time.time() - self.start_time:.2f} "
|
||||||
|
f"| remaining: {(self.total - self.current) * self.last_elapsed_time:.2f} ")
|
||||||
|
|
||||||
|
class Range:
|
||||||
|
def __init__(self, parent, iterable, update_interval):
|
||||||
|
self.iterable = iterable
|
||||||
|
self.parent = parent
|
||||||
|
self.update_interval = update_interval
|
||||||
|
|
||||||
|
self.parent.pbar['maximum'] = len(iterable)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
last_time = time.time()
|
||||||
|
last_update_time = 0
|
||||||
|
stats = AsyncProgress.Stats(len(self.iterable))
|
||||||
|
self.parent._start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
for obj in self.iterable:
|
||||||
|
yield obj
|
||||||
|
|
||||||
|
stats.current += 1
|
||||||
|
stats.last_elapsed_time = time.time() - last_time
|
||||||
|
last_time = time.time()
|
||||||
|
|
||||||
|
if time.time() - last_update_time > self.update_interval / 1000:
|
||||||
|
self.parent.step(1, stats)
|
||||||
|
last_update_time = time.time()
|
||||||
|
|
||||||
|
stats.total_elapsed_time = time.time() - stats.start_time
|
||||||
|
self.parent._finish(stats)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.iterable = range(0)
|
||||||
|
|
||||||
|
def __init__(self, parent, *,
|
||||||
|
width=450,
|
||||||
|
height=30,
|
||||||
|
update_interval=20,
|
||||||
|
range_update_interval=10,
|
||||||
|
label=None):
|
||||||
|
super().__init__(parent, width=width, height=height)
|
||||||
|
self.grid_propagate(False)
|
||||||
|
|
||||||
|
self.__event_step_queue = ClearableQueue()
|
||||||
|
self.__lock = threading.Lock()
|
||||||
|
self.__tk_pbar_value = tk.IntVar()
|
||||||
|
self.__tk_stats_str = tk.StringVar()
|
||||||
|
self.__tk_stats_str.set("Not running")
|
||||||
|
self.running = False
|
||||||
|
self.update_interval = update_interval
|
||||||
|
self.range_update_interval = range_update_interval
|
||||||
|
|
||||||
|
self.label = ttk.Label(self, text=label if label is not None else "")
|
||||||
|
self.pbar = ttk.Progressbar(self, variable=self.__tk_pbar_value)
|
||||||
|
self.stats = ttk.Label(self, textvariable=self.__tk_stats_str)
|
||||||
|
|
||||||
|
self.label.configure(font='TkFixedFont')
|
||||||
|
self.stats.configure(font='TkFixedFont')
|
||||||
|
|
||||||
|
self.label.grid(row=0, column=0, sticky=tk.NW)
|
||||||
|
self.pbar.grid(row=0, column=1, sticky=tk.NW)
|
||||||
|
self.stats.grid(row=0, column=2, sticky=tk.NW, padx=5)
|
||||||
|
|
||||||
|
self.__schedule_update()
|
||||||
|
|
||||||
|
def step(self, amount: int = 1, stat: Optional[Stats] = None):
|
||||||
|
self.__event_step_queue.put((amount, stat))
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
with self.__lock:
|
||||||
|
self.running = False
|
||||||
|
self.__event_step_queue.clear()
|
||||||
|
self.__tk_pbar_value.set(0)
|
||||||
|
self.__tk_stats_str.set("Not running")
|
||||||
|
|
||||||
|
def range(self, start_stop, stop=None, step=1):
|
||||||
|
with self.__lock:
|
||||||
|
if self.running:
|
||||||
|
raise RuntimeError('Progressbar is already running')
|
||||||
|
if stop is None:
|
||||||
|
stop = start_stop
|
||||||
|
start_stop = 0
|
||||||
|
return AsyncProgress.Range(self, range(start_stop, stop, step), self.range_update_interval)
|
||||||
|
|
||||||
|
def _start(self):
|
||||||
|
with self.__lock:
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
def _finish(self, stat):
|
||||||
|
with self.__lock:
|
||||||
|
self.running = False
|
||||||
|
self.__event_step_queue.clear()
|
||||||
|
self.__event_step_queue.put((0, stat))
|
||||||
|
|
||||||
|
def __schedule_update(self):
|
||||||
|
self.master.after(self.update_interval, self.__update_self)
|
||||||
|
|
||||||
|
def __update_self(self):
|
||||||
|
with acquire_timeout(self.__lock, 0.1):
|
||||||
|
while not self.__event_step_queue.empty():
|
||||||
|
(amount, stat) = self.__event_step_queue.get()
|
||||||
|
|
||||||
|
if stat is not None:
|
||||||
|
self.__tk_pbar_value.set(stat.current)
|
||||||
|
|
||||||
|
if self.running:
|
||||||
|
self.__tk_stats_str.set(stat)
|
||||||
|
else:
|
||||||
|
self.__tk_stats_str.set(
|
||||||
|
f'{stat.current}/{stat.total} | total: {stat.total_elapsed_time:.2f} seconds')
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.__tk_pbar_value.set(self.__tk_pbar_value.get() + amount)
|
||||||
|
self.__schedule_update()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
def worker(pbar, t):
|
||||||
|
print('Starting worker')
|
||||||
|
while True:
|
||||||
|
for i in pbar.range(10):
|
||||||
|
print(i)
|
||||||
|
time.sleep(t)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
pbar.reset()
|
||||||
|
time.sleep(1)
|
||||||
|
print("Finished worker" + str(t))
|
||||||
|
|
||||||
|
|
||||||
|
root = tk.Tk()
|
||||||
|
bar1 = AsyncProgress(root, label="Progressbar 1", update_interval=20, range_update_interval=10)
|
||||||
|
bar2 = AsyncProgress(root, label="Progressbar 2", update_interval=20, range_update_interval=10)
|
||||||
|
bar3 = AsyncProgress(root, label=None, update_interval=20, range_update_interval=10)
|
||||||
|
bar1.grid(row=0, column=0)
|
||||||
|
bar2.grid(row=1, column=0)
|
||||||
|
bar3.grid(row=2, column=0)
|
||||||
|
|
||||||
|
# worker(bar)
|
||||||
|
|
||||||
|
def start_worker():
|
||||||
|
threading.Thread(target=worker, args=(bar1, 0.10), daemon=True).start()
|
||||||
|
threading.Thread(target=worker, args=(bar2, 10.15), daemon=True).start()
|
||||||
|
threading.Thread(target=worker, args=(bar3, 0.05), daemon=True).start()
|
||||||
|
|
||||||
|
print("Worker threads started")
|
||||||
|
|
||||||
|
root.after(0, start_worker)
|
||||||
|
|
||||||
|
root.focus_set()
|
||||||
|
root.mainloop()
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
import torch
|
||||||
|
import torch.nn
|
||||||
|
|
||||||
|
|
||||||
|
class MyCNN(torch.nn.Module):
|
||||||
|
def __init__(self,
|
||||||
|
input_channels: int,
|
||||||
|
input_size: tuple[int, int],
|
||||||
|
hidden_channels: list[int],
|
||||||
|
output_channels: int,
|
||||||
|
use_batchnorm: bool,
|
||||||
|
kernel_size: list,
|
||||||
|
stride: list[int],
|
||||||
|
activation_function: torch.nn.Module = torch.nn.ReLU()):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
input_layer = torch.nn.Conv2d(in_channels=input_channels,
|
||||||
|
out_channels=hidden_channels[0],
|
||||||
|
kernel_size=kernel_size[0],
|
||||||
|
padding='same' if (stride[0] == 1 or stride[0] == 0) else 'valid',
|
||||||
|
stride=stride[0])
|
||||||
|
hidden_layers = [torch.nn.Conv2d(hidden_channels[i - 1],
|
||||||
|
hidden_channels[i],
|
||||||
|
kernel_size[i],
|
||||||
|
padding='same' if (stride[i] == 1 or stride[i] == 0) else 'valid',
|
||||||
|
stride=stride[i])
|
||||||
|
for i in range(1, len(hidden_channels))]
|
||||||
|
self.output_layer = torch.nn.Linear(hidden_channels[-1] * input_size[0] * input_size[1], output_channels)
|
||||||
|
|
||||||
|
def activation_function_repeater():
|
||||||
|
while True:
|
||||||
|
yield activation_function
|
||||||
|
|
||||||
|
layers_except_output = [input_layer,
|
||||||
|
*hidden_layers]
|
||||||
|
|
||||||
|
if use_batchnorm:
|
||||||
|
batch_norm_layers = [torch.nn.BatchNorm2d(hidden_channels[i]) for i in range(0, len(hidden_channels))]
|
||||||
|
# Adding an empty layer to not mess up list concatenation
|
||||||
|
batch_norm_layers = [*batch_norm_layers, torch.nn.BatchNorm2d(0)]
|
||||||
|
|
||||||
|
layers_except_output = [layer
|
||||||
|
for layer_tuple
|
||||||
|
in zip(layers_except_output, batch_norm_layers, activation_function_repeater())
|
||||||
|
for layer
|
||||||
|
in layer_tuple]
|
||||||
|
else:
|
||||||
|
layers_except_output = [layer
|
||||||
|
for layer_tuple in zip(layers_except_output, activation_function_repeater())
|
||||||
|
for layer in layer_tuple]
|
||||||
|
|
||||||
|
self.layers = torch.nn.Sequential(*layers_except_output)
|
||||||
|
|
||||||
|
def forward(self, input_images: torch.Tensor) -> torch.Tensor:
|
||||||
|
output = self.layers(input_images)
|
||||||
|
|
||||||
|
return self.output_layer(output.view(output.shape[0], -1))
|
||||||
|
|
||||||
|
# model = MyCNN()
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
import tkinter as tk
|
||||||
|
import warnings
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import numpy.random
|
||||||
|
import torch.utils.data
|
||||||
|
import torch.cuda
|
||||||
|
from tqdm.tk import tqdm
|
||||||
|
|
||||||
|
from architecture import MyCNN
|
||||||
|
from dataset import ImagesDataset
|
||||||
|
|
||||||
|
from AImageDataset import AImagesDataset
|
||||||
|
|
||||||
|
model = MyCNN(input_channels=1,
|
||||||
|
input_size=(100, 100),
|
||||||
|
hidden_channels=[500, 250, 100, 50],
|
||||||
|
output_channels=20,
|
||||||
|
use_batchnorm=True,
|
||||||
|
kernel_size=[9, 5, 3, 3, 1],
|
||||||
|
stride=[1, 1, 1, 1, 1],
|
||||||
|
activation_function=torch.nn.ReLU())
|
||||||
|
|
||||||
|
num_epochs = 100
|
||||||
|
|
||||||
|
batch_size = 64
|
||||||
|
optimizer = torch.optim.ASGD(model.parameters(),
|
||||||
|
lr=0.001,
|
||||||
|
lambd=1e-4,
|
||||||
|
alpha=0.75,
|
||||||
|
t0=1000000.0,
|
||||||
|
weight_decay=0)
|
||||||
|
loss_function = torch.nn.CrossEntropyLoss()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
torch.random.manual_seed(42)
|
||||||
|
numpy.random.seed(42)
|
||||||
|
|
||||||
|
start_time = datetime.now()
|
||||||
|
|
||||||
|
dataset = ImagesDataset("training_data")
|
||||||
|
|
||||||
|
# dataset = torch.utils.data.Subset(dataset, range(0, 20))
|
||||||
|
|
||||||
|
train_data, eval_data = torch.utils.data.random_split(dataset, [0.5, 0.5])
|
||||||
|
|
||||||
|
train_loader = torch.utils.data.DataLoader(AImagesDataset(train_data), batch_size=batch_size)
|
||||||
|
eval_loader = torch.utils.data.DataLoader(eval_data, batch_size=1)
|
||||||
|
|
||||||
|
if torch.cuda.is_available():
|
||||||
|
# print("GPU available")
|
||||||
|
model = model.cuda()
|
||||||
|
else:
|
||||||
|
warnings.warn("GPU not available")
|
||||||
|
|
||||||
|
train_losses = []
|
||||||
|
eval_losses = []
|
||||||
|
|
||||||
|
progress_epoch = tqdm(range(num_epochs), position=0, tk_parent=root_window)
|
||||||
|
progress_epoch.set_description("Epoch")
|
||||||
|
progress_train_data = tqdm(train_loader, position=1, tk_parent=root_window)
|
||||||
|
progress_eval_data = tqdm(eval_loader, position=2, tk_parent=root_window)
|
||||||
|
progress_train_data.set_description("Training progress")
|
||||||
|
progress_eval_data.set_description("Evaluation progress")
|
||||||
|
|
||||||
|
for epoch in progress_epoch:
|
||||||
|
|
||||||
|
train_loss = 0
|
||||||
|
eval_loss = 0
|
||||||
|
|
||||||
|
progress_train_data.reset()
|
||||||
|
progress_eval_data.reset()
|
||||||
|
|
||||||
|
# Start training of model
|
||||||
|
|
||||||
|
model.train()
|
||||||
|
|
||||||
|
for batch_nr, (imageT, transforms, img_index, classIDs, labels, paths) in enumerate(progress_train_data):
|
||||||
|
imageT = imageT.to('cuda')
|
||||||
|
classIDs = classIDs.to('cuda')
|
||||||
|
|
||||||
|
# progress_train_data.set_postfix_str("Running model...")
|
||||||
|
outputs = model(imageT)
|
||||||
|
|
||||||
|
optimizer.zero_grad()
|
||||||
|
# progress_train_data.set_postfix_str("calculating loss...")
|
||||||
|
loss = loss_function(outputs, classIDs)
|
||||||
|
# progress_train_data.set_postfix_str("propagating loss...")
|
||||||
|
loss.backward()
|
||||||
|
# progress_train_data.set_postfix_str("optimizing...")
|
||||||
|
optimizer.step()
|
||||||
|
|
||||||
|
train_loss += loss.item()
|
||||||
|
|
||||||
|
mean_loss = train_loss / len(train_loader)
|
||||||
|
train_losses.append(mean_loss)
|
||||||
|
|
||||||
|
# evaluation of model
|
||||||
|
|
||||||
|
model.eval()
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
for (imageT, classIDs, labels, paths) in progress_eval_data:
|
||||||
|
imageT = imageT.to('cuda')
|
||||||
|
classIDs = classIDs.to('cuda')
|
||||||
|
|
||||||
|
outputs = model(imageT)
|
||||||
|
loss = loss_function(outputs, classIDs)
|
||||||
|
eval_loss = loss.item()
|
||||||
|
|
||||||
|
eval_losses.append(eval_loss)
|
||||||
|
|
||||||
|
# print epoch summary
|
||||||
|
|
||||||
|
# print(f"Epoch: {epoch} --- Train loss: {train_loss:7.4f} --- Eval loss: {eval_loss:7.4f}")
|
||||||
|
|
||||||
|
torch.save(model.state_dict(), f'models/model-{start_time.strftime("%Y%m%d-%H%M%S")}-epoch-{epoch}.pt')
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
from glob import glob
|
||||||
|
from os import path
|
||||||
|
import os
|
||||||
|
import torch
|
||||||
|
from typing import Optional
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
from torch.utils.data import Dataset
|
||||||
|
|
||||||
|
class ImagesDataset(Dataset):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
image_dir,
|
||||||
|
width: int = 100,
|
||||||
|
height: int = 100,
|
||||||
|
dtype: Optional[type] = None
|
||||||
|
):
|
||||||
|
self.image_filepaths = sorted(path.abspath(f) for f in glob(path.join(image_dir, "*.jpg")))
|
||||||
|
class_filepath = [path.abspath(f) for f in glob(path.join(image_dir, "*.csv"))][0]
|
||||||
|
self.filenames_classnames, self.classnames_to_ids = ImagesDataset.load_classnames(class_filepath)
|
||||||
|
if width < 100 or height < 100:
|
||||||
|
raise ValueError('width and height must be greater than or equal 100')
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.dtype = dtype
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_classnames(class_filepath: str):
|
||||||
|
filenames_classnames = np.genfromtxt(class_filepath, delimiter=';', skip_header=1, dtype=str)
|
||||||
|
classnames = np.unique(filenames_classnames[:, 1])
|
||||||
|
classnames.sort()
|
||||||
|
classnames_to_ids = {}
|
||||||
|
for index, classname in enumerate(classnames):
|
||||||
|
classnames_to_ids[classname] = index
|
||||||
|
return filenames_classnames, classnames_to_ids
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
with Image.open(self.image_filepaths[index]) as im:
|
||||||
|
image = np.array(im, dtype=self.dtype)
|
||||||
|
image = to_grayscale(image)
|
||||||
|
resized_image, _ = prepare_image(image, self.width, self.height, 0, 0, 32)
|
||||||
|
resized_image = torch.tensor(resized_image, dtype=torch.float32)/255.0
|
||||||
|
classname = self.filenames_classnames[index][1]
|
||||||
|
classid = self.classnames_to_ids[classname]
|
||||||
|
return resized_image, classid, classname, self.image_filepaths[index]
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.image_filepaths)
|
||||||
|
|
||||||
|
def to_grayscale(pil_image: np.ndarray) -> np.ndarray:
|
||||||
|
if pil_image.ndim == 2:
|
||||||
|
return pil_image.copy()[None]
|
||||||
|
if pil_image.ndim != 3:
|
||||||
|
raise ValueError("image must have either shape (H, W) or (H, W, 3)")
|
||||||
|
if pil_image.shape[2] != 3:
|
||||||
|
raise ValueError(f"image has shape (H, W, {pil_image.shape[2]}), but it should have (H, W, 3)")
|
||||||
|
|
||||||
|
rgb = pil_image / 255
|
||||||
|
rgb_linear = np.where(
|
||||||
|
rgb < 0.04045,
|
||||||
|
rgb / 12.92,
|
||||||
|
((rgb + 0.055) / 1.055) ** 2.4
|
||||||
|
)
|
||||||
|
grayscale_linear = 0.2126 * rgb_linear[..., 0] + 0.7152 * rgb_linear[..., 1] + 0.0722 * rgb_linear[..., 2]
|
||||||
|
|
||||||
|
grayscale = np.where(
|
||||||
|
grayscale_linear < 0.0031308,
|
||||||
|
12.92 * grayscale_linear,
|
||||||
|
1.055 * grayscale_linear ** (1 / 2.4) - 0.055
|
||||||
|
)
|
||||||
|
grayscale = grayscale * 255
|
||||||
|
|
||||||
|
if np.issubdtype(pil_image.dtype, np.integer):
|
||||||
|
grayscale = np.round(grayscale)
|
||||||
|
return grayscale.astype(pil_image.dtype)[None]
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_image(image: np.ndarray, width: int, height: int, x: int, y: int, size: int):
|
||||||
|
if image.ndim < 3 or image.shape[-3] != 1:
|
||||||
|
raise ValueError("image must have shape (1, H, W)")
|
||||||
|
if width < 32 or height < 32 or size < 32:
|
||||||
|
raise ValueError("width/height/size must be >= 32")
|
||||||
|
if x < 0 or (x + size) > width:
|
||||||
|
raise ValueError(f"x={x} and size={size} do not fit into the resized image width={width}")
|
||||||
|
if y < 0 or (y + size) > height:
|
||||||
|
raise ValueError(f"y={y} and size={size} do not fit into the resized image height={height}")
|
||||||
|
|
||||||
|
image = image.copy()
|
||||||
|
|
||||||
|
if image.shape[1] > height:
|
||||||
|
image = image[:, (image.shape[1] - height) // 2: (image.shape[1] - height) // 2 + height, :]
|
||||||
|
else:
|
||||||
|
image = np.pad(image, ((0, 0), ((height - image.shape[1])//2, math.ceil((height - image.shape[1])/2)), (0, 0)), mode='edge')
|
||||||
|
|
||||||
|
if image.shape[2] > width:
|
||||||
|
image = image[:, :, (image.shape[2] - width) // 2: (image.shape[2] - width) // 2 + width]
|
||||||
|
else:
|
||||||
|
image = np.pad(image, ((0, 0), (0, 0), ((width - image.shape[2])//2, math.ceil((width - image.shape[2])/2))), mode='edge')
|
||||||
|
|
||||||
|
subarea = image[:, y:y + size, x:x + size]
|
||||||
|
return image, subarea
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import tkinter as tk
|
||||||
|
import tkinter.ttk as ttk
|
||||||
|
|
||||||
|
from AsyncProgress import AsyncProgress
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
root = tk.Tk()
|
||||||
|
root.title("AI Project")
|
||||||
|
|
||||||
|
progress_frame = ttk.Frame(root, padding=20, width=500)
|
||||||
|
progress_frame.grid(row=0, column=0)
|
||||||
|
pbar_epoch = AsyncProgress(progress_frame, label=" Epoch: ")
|
||||||
|
pbar_train = AsyncProgress(progress_frame, label=" Train: ")
|
||||||
|
pbar_eval = AsyncProgress(progress_frame, label="Evaluation: ")
|
||||||
|
|
||||||
|
pbar_epoch.grid(row=0, column=0)
|
||||||
|
pbar_train.grid(row=1, column=0)
|
||||||
|
pbar_eval.grid(row=2, column=0)
|
||||||
|
|
||||||
|
root.focus_set()
|
||||||
|
root.mainloop()
|
||||||
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.0 KiB |