Skip to content

Commit f00e73e

Browse files
authored
Add the changes to comply with the project specifications. (#31)
* Added a first implementation of conv2d * Added the non-optimized implementation of conv2d We maintained the same names of attributes to make it compatible with the checkpoints. * Implemented a simulation of random_split of pytorch * Implemented a simulation of PixelShuffle module Given the previous layer return the right dimensions of the tensor, the pixel shuffle is just a reshaping. * Changed the bias to a tensor of zeros when bias=False This change is just for get rid of the warning that int has no __get_item__ implementation * Added a im2col fast convolution and fixed PixelShuffle * Added small model training It only upscales 32x16 ==> 64x32 * Changed constructor of SuperResolutionDataset Added 2 parameters to change the low and high resolution of the loading. Default values are 128x64 ==> 256x128 * Added declaration for small dataset training * Added old validation parameters * Added in the notebook the comparison between models * Changed notebook date generation * Added notebook plots * Added generalization to choose resolutions * Changed return statement * Edited the report to include both models * Deleted old files
1 parent 2bd3904 commit f00e73e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1085
-419
lines changed

.github/workflows/CI.yml

Lines changed: 0 additions & 35 deletions
This file was deleted.

.idea/Super-Resolution.iml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ModelDemonstration.pdf

572 KB
Binary file not shown.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ python3 main.py
3838
# Export the notebook as pdf
3939

4040
```bash
41-
jupyter nbconvert --to pdf notebook.ipynb --output "ModelDemonstration" --LatexPreprocessor.title "Super Resolution Demonstration" --LatexPreprocessor.date "August, 2024" --LatexPreprocessor.author_names "Christian Mancini"
41+
jupyter nbconvert --to pdf notebook.ipynb --output "ModelDemonstration" --LatexPreprocessor.title "Super Resolution Demonstration" --LatexPreprocessor.date "September, 2024" --LatexPreprocessor.author_names "Christian Mancini"
4242
```
4343

4444
# Installation of Requirements and Kernel

SRM/modules.py

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
1+
import torch
12
from torch import nn
3+
import numpy as np
24

35

6+
def _im2col(input, kernel_size, stride=1, padding=0):
7+
input_padded = torch.nn.functional.pad(input, (padding, padding, padding, padding))
8+
batch_size, in_channels, height, width = input_padded.size()
9+
kernel_height, kernel_width = kernel_size
10+
out_height = (height - kernel_height) // stride + 1
11+
out_width = (width - kernel_width) // stride + 1
12+
col = torch.empty(batch_size, in_channels, kernel_height, kernel_width, out_height, out_width)
13+
14+
for y in range(kernel_height):
15+
for x in range(kernel_width):
16+
col[:, :, y, x, :, :] = input_padded[:, :, y: y + out_height * stride: stride,
17+
x: x + out_width * stride: stride]
18+
19+
return col.view(batch_size, in_channels * kernel_height * kernel_width, -1)
20+
421
class ResidualBlock(nn.Module):
522

623
def __init__(self, num_channels):
724
super().__init__()
8-
self.conv1 = nn.Conv2d(num_channels, num_channels, kernel_size=3, padding=1)
25+
self.conv1 = Conv2d(num_channels, num_channels, kernel_size=3, padding=1)
926
self.relu = nn.ReLU()
10-
self.conv2 = nn.Conv2d(num_channels, num_channels, kernel_size=3, padding=1)
27+
self.conv2 = Conv2d(num_channels, num_channels, kernel_size=3, padding=1)
1128

1229
def forward(self, x):
1330
res = x
@@ -22,11 +39,80 @@ class Upsample(nn.Module):
2239

2340
def __init__(self, num_channel):
2441
super().__init__()
25-
self.conv = nn.Conv2d(num_channel, num_channel * 4, kernel_size=3, padding=1)
26-
self.shuffle = nn.PixelShuffle(2)
42+
self.conv = Conv2d(num_channel, num_channel * 4, kernel_size=3, padding=1)
43+
self.shuffle = PixelShuffle(2)
2744

2845
def forward(self, x):
2946
out = self.conv(x)
3047
out = self.shuffle(out)
3148
return out
3249

50+
51+
class Conv2d(nn.Module):
52+
def __init__(self, in_channels: int, out_channels: int, kernel_size: int, padding=1, bias=True):
53+
super().__init__()
54+
self.in_channels = in_channels
55+
self.out_channels = out_channels
56+
self.kernel_size = kernel_size
57+
self.padding = padding
58+
self.k = 1/(in_channels * np.power(kernel_size, 2))
59+
60+
if bias:
61+
self.bias = nn.Parameter(
62+
torch.empty(out_channels).uniform_(-np.sqrt(self.k),np.sqrt(self.k)))
63+
else:
64+
self.bias = torch.zeros(out_channels)
65+
66+
self.weight = nn.Parameter(
67+
torch.empty(
68+
out_channels, in_channels, kernel_size, kernel_size).uniform_(-np.sqrt(self.k),np.sqrt(self.k)))
69+
70+
71+
def _conv_forward(self, input, weight, bias=None, stride=1, padding=0):
72+
col = _im2col(input, weight.size()[2:], stride, padding)
73+
# (out_channels, in_channels * kernel_height * kernel_width)
74+
weight_col = weight.view(weight.size(0), -1)
75+
out = torch.matmul(weight_col, col)
76+
77+
if bias is not None:
78+
out += bias.view(1, -1, 1)
79+
80+
batch_size, out_channels = out.size(0), weight.size(0)
81+
out_height = (input.size(2) + 2 * padding - weight.size(2)) // stride + 1
82+
out_width = (input.size(3) + 2 * padding - weight.size(3)) // stride + 1
83+
return out.view(batch_size, out_channels, out_height, out_width)
84+
85+
def forward(self, input):
86+
return self._conv_forward(input, self.weight, self.bias, padding=self.padding)
87+
88+
89+
def slow_forward(self, image):
90+
image = nn.functional.pad(image, (self.padding,) * 4, "constant", 0)
91+
batch_size, in_channels, height, width = image.shape
92+
out_channels, in_channels_kernel, m, n = self.weight.shape
93+
if self.in_channels != in_channels:
94+
raise ValueError(
95+
f"Input channels are different: Declared {self.in_channels}, but got Image with {in_channels}")
96+
output_height = height - m + 1
97+
output_width = width - n + 1
98+
new_image = torch.zeros((batch_size, out_channels, output_height, output_width))
99+
100+
for b in range(batch_size):
101+
for c in range(out_channels):
102+
for i in range(output_height):
103+
for j in range(output_width):
104+
new_image[b, c, i, j] = torch.sum(image[b, :, i:i + m, j:j + n] * self.weight[c]) + self.bias[c]
105+
return new_image
106+
107+
108+
class PixelShuffle(nn.Module):
109+
def __init__(self, upscale_factor: int):
110+
super().__init__()
111+
self.upscale_factor = upscale_factor
112+
113+
def forward(self, x):
114+
batch_size, channels, height, width = x.shape
115+
channels //= (self.upscale_factor ** 2)
116+
x = x.view(batch_size, channels, self.upscale_factor, self.upscale_factor, height, width)
117+
x = x.permute(0, 1, 4, 2, 5, 3)
118+
return x.contiguous().view(batch_size, channels, height * self.upscale_factor, width * self.upscale_factor)

SRM/network.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ class SuperResolution(nn.Module):
99
def __init__(self, num_channels: int, num_res_block: int):
1010
super().__init__()
1111
self.layers = nn.ModuleList([
12-
nn.Conv2d(3, num_channels, kernel_size=3, padding=1)
12+
Conv2d(3, num_channels, kernel_size=3, padding=1)
1313
])
1414
for _ in range(num_res_block):
1515
self.layers.append(ResidualBlock(num_channels))
1616
self.layers.append(Upsample(num_channels))
17-
self.layers.append(nn.Conv2d(num_channels, 3, kernel_size=3, padding=1))
17+
self.layers.append(Conv2d(num_channels, 3, kernel_size=3, padding=1))
1818

1919
def forward(self, x):
2020
for layer in self.layers:
2.84 MB
Binary file not shown.
2.84 MB
Binary file not shown.

dataset/data_preparation.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,28 @@ def download(path: str, dataset: str):
2929
print(f"Dataset {dataset} already exists, skipping download.")
3030

3131

32+
def _random_split(dataset: Dataset, lengths: list[int]) -> list[Subset]:
33+
"""
34+
Utility method that simulate pytorch random split
35+
Args:
36+
dataset: Dataset to be split
37+
lengths: List of lengths to split the dataset into
38+
39+
Returns:
40+
List of Subsets
41+
"""
42+
if sum(lengths) != len(dataset):
43+
raise ValueError("Sum of input lengths does not equal the length of the input dataset!")
44+
45+
indices = torch.randperm(len(dataset)).tolist()
46+
subsets = []
47+
offset = 0
48+
for length in lengths:
49+
subset = torch.utils.data.Subset(dataset, indices[offset:offset + length])
50+
subsets.append(subset)
51+
offset += length
52+
return subsets
53+
3254
def split_dataset(dataset: Dataset, sizes: dict[str, float]) -> list[Subset]:
3355
"""
3456
Splits a dataset into training, validation, and test sets based on the provided sizes.
@@ -52,4 +74,4 @@ def split_dataset(dataset: Dataset, sizes: dict[str, float]) -> list[Subset]:
5274
train_size = int(sizes["train"] * len(dataset))
5375
validation_size = int(sizes["validation"] * len(dataset))
5476
test_size = len(dataset) - train_size - validation_size
55-
return torch.utils.data.random_split(dataset, [train_size, validation_size, test_size])
77+
return _random_split(dataset, [train_size, validation_size, test_size])

dataset/super_resolution_dataset.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77

88
class SuperResolutionDataset(Dataset):
9-
def __init__(self, root_dir, transform=transforms.Compose([transforms.ToTensor()])) -> None:
9+
def __init__(self, root_dir, low_resolution=(128, 64), high_resolution=(256, 128),
10+
transform=transforms.Compose([transforms.ToTensor()])) -> None:
1011
self.root_dir = root_dir
1112
self.transform = transform
1213
self.file_names = os.listdir(root_dir)
14+
self.low_resolution = low_resolution
15+
self.high_resolution = high_resolution
1316

1417
def __len__(self) -> int:
1518
return len(self.file_names)
@@ -25,8 +28,8 @@ def __getitem__(self, idx) -> tuple[torch.Tensor, torch.Tensor]:
2528
"""
2629
img_path = os.path.join(self.root_dir, self.file_names[idx])
2730
image = Image.open(img_path)
28-
low_res = image.resize((128, 64))
29-
high_res = image.resize((256, 128))
31+
low_res = image.resize(self.low_resolution)
32+
high_res = image.resize(self.high_resolution)
3033

3134
if self.transform:
3235
low_res = self.transform(low_res)

0 commit comments

Comments
 (0)