R2454 commited on
Commit
32e0f95
·
1 Parent(s): 46fe4d9

Added Multiple things

Browse files
app.py ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from PIL import Image
3
+ import os
4
+ import numpy as np
5
+ import cv2
6
+
7
+ # Existing imports
8
+ from models.lancros_interpolation import upsample_lancros
9
+ from models.fourier_interpolation import fourier_upscale
10
+ from models.autoencoder import autoencoder_upscale
11
+ from models.espcn import espcn_upscale
12
+ from models.sr_gan import srgan_upscale
13
+ from models.cnn import srcnn_upscale
14
+
15
+ # ✅ New import for Random Forest Super Resolution
16
+ from models.random_forest_sr import random_forest_upscale
17
+
18
+ # ✅ EDI (Edge Directed Interpolation) method
19
+ from PIL import Image
20
+ import numpy as np
21
+
22
+ from PIL import Image
23
+ import numpy as np
24
+ from models.rcan import rcan_upscale
25
+
26
+ def edge_directed_interpolation(lr_img_pil, scale=2):
27
+ # Ensure input is a PIL Image
28
+
29
+ if isinstance(lr_img_pil, np.ndarray):
30
+ lr_img_pil = Image.fromarray(lr_img_pil)
31
+
32
+ # Convert to RGB
33
+ lr_img_rgb = lr_img_pil.convert("RGB")
34
+ lr_img = np.array(lr_img_rgb)
35
+
36
+ h, w, c = lr_img.shape
37
+ hr_h, hr_w = h * scale, w * scale
38
+ hr_img = np.zeros((hr_h, hr_w, c), dtype=np.uint8)
39
+
40
+ # Copy original pixels to even positions
41
+ for i in range(h):
42
+ for j in range(w):
43
+ hr_img[i * scale, j * scale] = lr_img[i, j]
44
+
45
+ # Interpolate diagonal pixels per channel
46
+ for i in range(0, hr_h, scale):
47
+ for j in range(0, hr_w, scale):
48
+ if i + scale < hr_h and j + scale < hr_w:
49
+ for ch in range(c):
50
+ p1 = hr_img[i, j, ch]
51
+ p2 = hr_img[i, j + scale, ch]
52
+ p3 = hr_img[i + scale, j, ch]
53
+ p4 = hr_img[i + scale, j + scale, ch]
54
+
55
+ d1 = abs(int(p1) - int(p4))
56
+ d2 = abs(int(p2) - int(p3))
57
+
58
+ interp = (int(p1) + int(p4)) // 2 if d1 < d2 else (int(p2) + int(p3)) // 2
59
+ hr_img[i + scale // 2, j + scale // 2, ch] = interp
60
+
61
+ # Fill remaining zero pixels per channel
62
+ for i in range(hr_h):
63
+ for j in range(hr_w):
64
+ for ch in range(c):
65
+ if hr_img[i, j, ch] == 0:
66
+ neighbors = []
67
+ if i - 1 >= 0:
68
+ neighbors.append(hr_img[i - 1, j, ch])
69
+ if i + 1 < hr_h:
70
+ neighbors.append(hr_img[i + 1, j, ch])
71
+ if j - 1 >= 0:
72
+ neighbors.append(hr_img[i, j - 1, ch])
73
+ if j + 1 < hr_w:
74
+ neighbors.append(hr_img[i, j + 1, ch])
75
+ if neighbors:
76
+ hr_img[i, j, ch] = int(np.mean(neighbors))
77
+
78
+ return Image.fromarray(hr_img)
79
+
80
+
81
+
82
+ # === Interfaces === #
83
+ lancros_page = gr.Interface(
84
+ fn=upsample_lancros,
85
+ inputs=[gr.Image(label="Low Resolution Image"),
86
+ gr.Slider(2, 6, step=1, value=2, label="Upscaling Factor"),],
87
+ outputs=gr.Image(type="pil", label="High Resolution Images"),
88
+ title="Lancros Upsampling",
89
+ examples=[
90
+ ["sample_images/0001.png"],
91
+ ["sample_images/0172.png"]
92
+ ]
93
+ )
94
+
95
+ fourier_page = gr.Interface(
96
+ fn=fourier_upscale,
97
+ inputs=[gr.Image(label="Low Resolution Image"),
98
+ gr.Slider(2, 6, step=1, value=2, label="Upscaling Factor"),],
99
+ outputs=gr.Image(type="pil", label="High Resolution Images"),
100
+ title="Fourier Upsampling",
101
+ examples=[
102
+ ["sample_images/0004.png"],
103
+ ["sample_images/0012.png"]
104
+ ]
105
+ )
106
+
107
+ autoencoder_page = gr.Interface(
108
+ fn=autoencoder_upscale,
109
+ inputs=[gr.Image(label="Low Resolution Image")],
110
+ outputs=gr.Image(type="pil", label="High Resolution Images"),
111
+ title="Autoencoder based Super Resolution",
112
+ examples=[
113
+ ["sample_images/0019.png"],
114
+ ["sample_images/0064.png"]
115
+ ]
116
+ )
117
+
118
+ espcn_page = gr.Interface(
119
+ fn=espcn_upscale,
120
+ inputs=[gr.Image(label="Low Resolution Image")],
121
+ outputs=gr.Image(type="pil", label="High Resolution Images"),
122
+ title="ESPCN based Super Resolution",
123
+ examples=[
124
+ ["sample_images/0024.png"],
125
+ ["sample_images/0068.png"]
126
+ ]
127
+ )
128
+
129
+ srgan_page = gr.Interface(
130
+ fn=srgan_upscale,
131
+ inputs=[gr.Image(label="Low Resolution Image")],
132
+ outputs=gr.Image(type="pil", label="High Resolution Images"),
133
+ title="GAN based Super Resolution",
134
+ examples=[
135
+ ["sample_images/0055.png"],
136
+ ["sample_images/0003.png"]
137
+ ]
138
+ )
139
+
140
+ random_forest_page = gr.Interface(
141
+ fn=random_forest_upscale,
142
+ inputs=[gr.Image(label="Low Resolution Image")],
143
+ outputs=gr.Image(type="pil", label="High Resolution Images"),
144
+ title="Random Forest based Super Resolution",
145
+ examples=[
146
+ ["sample_images/0097.png"],
147
+ ["sample_images/0086.png"]
148
+ ]
149
+ )
150
+
151
+ # ✅ EDI Page
152
+ edi_page = gr.Interface(
153
+ fn=edge_directed_interpolation,
154
+ inputs=[gr.Image(label="Low Resolution Image"), gr.Slider(2, 4, step=1, value=2, label="Upscaling Factor")],
155
+ outputs=gr.Image(type="pil", label="High Resolution Image"),
156
+ title="Edge Directed Interpolation",
157
+ examples=[
158
+ ["sample_images/0002.png"],
159
+ ["sample_images/0006.png"]
160
+ ]
161
+ )
162
+
163
+ rcan_page = gr.Interface(
164
+ fn=rcan_upscale,
165
+ inputs=[gr.Image(label="Low Resolution Image")],
166
+ outputs=gr.Image(type="pil", label="High Resolution Image"),
167
+ title="RCAN based Super Resolution",
168
+ examples=[
169
+ ["sample_images/0007.png"],
170
+ ["sample_images/0010.png"]
171
+ ]
172
+ )
173
+
174
+ srcnn_page = gr.Interface(
175
+ fn=srcnn_upscale,
176
+ inputs=[gr.Image(label="Low Resolution Image")],
177
+ outputs=gr.Image(type="pil", label="High Resolution Image"),
178
+ title="SRCNN based Super Resolution",
179
+ examples=[
180
+ ["sample_images/0007.png"],
181
+ ["sample_images/0010.png"]
182
+ ]
183
+ )
184
+
185
+ # Tabs setup
186
+ demo = gr.TabbedInterface(
187
+ [srgan_page, lancros_page, fourier_page, autoencoder_page, espcn_page, random_forest_page, edi_page, rcan_page,srcnn_page],
188
+ ["GAN based Super Resolution", "Lancros Interpolation", "Fourier Interpolation", "Autoencoder based Super Resolution",
189
+ "EspCN Super Resolution", "Random Forest based Super Resolution", "Edge Directed Interpolation", "RCAN Super Resolution","SRCNN Super Resolution"],
190
+ title="Image Super Resolution"
191
+ )
192
+
193
+ if __name__ == "__main__":
194
+ demo.launch(debug=True)
models/__pycache__/autoencoder.cpython-312.pyc ADDED
Binary file (3.73 kB). View file
 
models/__pycache__/fourier_interpolation.cpython-312.pyc ADDED
Binary file (1.66 kB). View file
 
models/__pycache__/lancros.cpython-312.pyc ADDED
Binary file (3.28 kB). View file
 
models/__pycache__/lancros_interpolation.cpython-312.pyc ADDED
Binary file (3.29 kB). View file
 
models/__pycache__/sr_gan.cpython-312.pyc ADDED
Binary file (4.92 kB). View file
 
models/autoencoder.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from collections import OrderedDict
4
+ from PIL import Image,ImageOps
5
+ import numpy as np
6
+
7
+ class Autoencoder(nn.Module):
8
+ def __init__(self):
9
+ super().__init__()
10
+
11
+ # ENCODER — compress from 128 -> 64 -> 32
12
+ self.enc1 = nn.Conv2d(3, 64, 3, stride=2, padding=1) # 128 → 64
13
+ self.enc2 = nn.Conv2d(64, 128, 3, stride=2, padding=1) # 64 → 32
14
+
15
+ # DECODER — upsample from 32 → 64 → 128 → 256 → 512
16
+ self.dec1 = nn.ConvTranspose2d(128, 64, 3, stride=2, padding=1, output_padding=1) # 32 → 64
17
+ self.dec2 = nn.ConvTranspose2d(64, 32, 3, stride=2, padding=1, output_padding=1) # 64 → 128
18
+ self.dec3 = nn.ConvTranspose2d(32, 16, 3, stride=2, padding=1, output_padding=1) # 128 → 256
19
+ self.dec4 = nn.ConvTranspose2d(16, 3, 3, stride=2, padding=1, output_padding=1) # 256 → 512
20
+
21
+ # Activations
22
+ self.relu = nn.ReLU()
23
+ self.sigmoid = nn.Sigmoid()
24
+
25
+ def forward(self, x):
26
+ # Encoder
27
+ x = self.relu(self.enc1(x)) # [B, 64, 64, 64]
28
+ x = self.relu(self.enc2(x)) # [B, 128, 32, 32]
29
+
30
+ # Decoder
31
+ x = self.relu(self.dec1(x)) # [B, 64, 64, 64]
32
+ x = self.relu(self.dec2(x)) # [B, 32, 128, 128]
33
+ x = self.relu(self.dec3(x)) # [B, 16, 256, 256]
34
+ x = self.sigmoid(self.dec4(x)) # [B, 3, 512, 512]
35
+
36
+ return x
37
+
38
+ def autoencoder_upscale(image):
39
+ image = Image.fromarray(image)
40
+ target_size = (128, 128)
41
+ pad_color=(0, 0, 0)
42
+ ImageOps.pad(image, target_size, method=Image.BICUBIC, color=pad_color)
43
+ image = np.array(image)
44
+
45
+ image = image/255
46
+ image = torch.from_numpy(image).float().unsqueeze(dim=0).permute(0,3,1,2)
47
+
48
+ model = Autoencoder()
49
+ checkpoint = torch.load("weights/model_auto_2.pth", map_location=torch.device('cpu')) # or 'cuda' if using GPU
50
+ state_dict = checkpoint['state_dict']
51
+
52
+ new_state_dict = OrderedDict()
53
+ for k, v in state_dict.items():
54
+ new_key = k.replace("module.", "") # Remove "module." from each key
55
+ new_state_dict[new_key] = v
56
+
57
+ model.load_state_dict(new_state_dict)
58
+ model.eval()
59
+
60
+ with torch.no_grad():
61
+ output = model(image)
62
+
63
+ output = output.squeeze(0).permute(1, 2, 0).cpu().numpy()
64
+ output = (output * 255.0).clip(0, 255).astype("uint8")
65
+ return output
models/cnn.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from collections import OrderedDict
4
+
5
+ # SRCNN Model Definition
6
+ class SuperResolutionCNN(nn.Module):
7
+ def __init__(self):
8
+ super(SuperResolutionCNN, self).__init__()
9
+
10
+ # Feature extraction
11
+ self.features = nn.Sequential(
12
+ nn.Conv2d(3, 64, kernel_size=9, padding=4),
13
+ nn.ReLU(),
14
+ nn.Conv2d(64, 32, kernel_size=1, padding=0),
15
+ nn.ReLU()
16
+ )
17
+
18
+ # Upsampling blocks
19
+ self.upsample = nn.Sequential(
20
+ nn.ConvTranspose2d(32, 32, kernel_size=3, stride=2, padding=1, output_padding=1),
21
+ nn.ReLU(),
22
+ nn.ConvTranspose2d(32, 32, kernel_size=3, stride=2, padding=1, output_padding=1),
23
+ nn.ReLU()
24
+ )
25
+
26
+ # Reconstruction
27
+ self.reconstruction = nn.Conv2d(32, 3, kernel_size=5, padding=2)
28
+
29
+ def forward(self, x):
30
+ x = self.features(x)
31
+ x = self.upsample(x)
32
+ x = self.reconstruction(x)
33
+ return torch.sigmoid(x)
34
+
35
+
36
+ def srcnn_upscale(image, model_path="weights/model_cnn.pth", device='cpu'):
37
+ """
38
+ Upscale image using SRCNN model
39
+ Input: numpy array (H,W,3) in range 0-255
40
+ Output: numpy array (H,W,3) in range 0-255
41
+
42
+ Args:
43
+ image: Input image as numpy array (H,W,3) in range 0-255
44
+ model_path: Path to the trained SRCNN model weights
45
+ device: 'cpu' or 'cuda' for GPU acceleration
46
+ """
47
+ # Normalize and convert to tensor [1,3,H,W]
48
+ image = image / 255.0
49
+ image = torch.from_numpy(image).float().unsqueeze(0).permute(0, 3, 1, 2).to(device)
50
+
51
+ # Load model
52
+ model = SuperResolutionCNN().to(device)
53
+
54
+ # Load weights
55
+ checkpoint = torch.load(model_path, map_location=torch.device(device))
56
+
57
+ # Handle different checkpoint formats
58
+ if 'state_dict' in checkpoint:
59
+ state_dict = checkpoint['state_dict']
60
+ elif 'model_state_dict' in checkpoint:
61
+ state_dict = checkpoint['model_state_dict']
62
+ else:
63
+ state_dict = checkpoint # Assume direct state dict
64
+
65
+ # Remove "module." prefix if present (for DataParallel models)
66
+ new_state_dict = OrderedDict()
67
+ for k, v in state_dict.items():
68
+ new_key = k.replace("module.", "")
69
+ new_state_dict[new_key] = v
70
+
71
+ model.load_state_dict(new_state_dict)
72
+ model.eval()
73
+
74
+ # Process image
75
+ with torch.no_grad():
76
+ output = model(image)
77
+
78
+ # Convert back to numpy array [H,W,3] 0-255
79
+ output = output.squeeze(0).permute(1, 2, 0).cpu().numpy()
80
+ output = (output * 255.0).clip(0, 255).astype("uint8")
81
+
82
+ return output
models/edge_directed_interpolation.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+
4
+ def edge_directed_interpolation(lr_img_pil, scale=2):
5
+ lr_img = np.array(lr_img_pil.convert("L"))
6
+ h, w = lr_img.shape
7
+ hr_h, hr_w = h * scale, w * scale
8
+
9
+ hr_img = np.zeros((hr_h, hr_w), dtype=np.uint8)
10
+
11
+ for i in range(h):
12
+ for j in range(w):
13
+ hr_img[i * scale, j * scale] = lr_img[i, j]
14
+
15
+ for i in range(0, hr_h, scale):
16
+ for j in range(0, hr_w, scale):
17
+ if i + scale < hr_h and j + scale < hr_w:
18
+ p1 = hr_img[i, j]
19
+ p2 = hr_img[i, j + scale]
20
+ p3 = hr_img[i + scale, j]
21
+ p4 = hr_img[i + scale, j + scale]
22
+
23
+ d1 = abs(int(p1) - int(p4))
24
+ d2 = abs(int(p2) - int(p3))
25
+
26
+ if d1 < d2:
27
+ interp = (int(p1) + int(p4)) // 2
28
+ else:
29
+ interp = (int(p2) + int(p3)) // 2
30
+
31
+ hr_img[i + scale // 2, j + scale // 2] = interp
32
+
33
+ for i in range(hr_h):
34
+ for j in range(hr_w):
35
+ if hr_img[i, j] == 0:
36
+ neighbors = []
37
+ if i - 1 >= 0:
38
+ neighbors.append(hr_img[i - 1, j])
39
+ if i + 1 < hr_h:
40
+ neighbors.append(hr_img[i + 1, j])
41
+ if j - 1 >= 0:
42
+ neighbors.append(hr_img[i, j - 1])
43
+ if j + 1 < hr_w:
44
+ neighbors.append(hr_img[i, j + 1])
45
+ if neighbors:
46
+ hr_img[i, j] = np.mean(neighbors).astype(np.uint8)
47
+
48
+ hr_img_pil = Image.fromarray(hr_img)
49
+ return hr_img_pil
models/espcn.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ from collections import OrderedDict
5
+
6
+ class ESPCN(nn.Module):
7
+ def __init__(self):
8
+ super(ESPCN, self).__init__()
9
+ self.conv1 = nn.Conv2d(3, 64, kernel_size=5, padding=2)
10
+ self.conv2 = nn.Conv2d(64, 32, kernel_size=3, padding=1)
11
+ self.conv3 = nn.Conv2d(32, (4 ** 2) * 3, kernel_size=3, padding=1) # Handle 3-channel output
12
+ self.pixel_shuffle = nn.PixelShuffle(4)
13
+
14
+ def forward(self, x):
15
+ x = F.relu(self.conv1(x))
16
+ x = F.relu(self.conv2(x))
17
+ x = self.pixel_shuffle(self.conv3(x))
18
+ return x
19
+
20
+ def espcn_upscale(image):
21
+ image = image/255
22
+ image = torch.from_numpy(image).float().unsqueeze(dim=0).permute(0,3,1,2)
23
+
24
+ model = ESPCN()
25
+ checkpoint = torch.load("weights/model_espcn.pth", map_location=torch.device('cpu')) # or 'cuda' if using GPU
26
+ state_dict = checkpoint['state_dict']
27
+
28
+ new_state_dict = OrderedDict()
29
+ for k, v in state_dict.items():
30
+ new_key = k.replace("module.", "") # Remove "module." from each key
31
+ new_state_dict[new_key] = v
32
+
33
+ model.load_state_dict(new_state_dict)
34
+ model.eval()
35
+
36
+ with torch.no_grad():
37
+ output = model(image)
38
+
39
+ output = output.squeeze(0).permute(1, 2, 0).cpu().numpy()
40
+ output = (output * 255.0).clip(0, 255).astype("uint8")
41
+ return output
models/fourier_interpolation.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+
4
+ from scipy.fft import fft2, ifft2, fftshift, ifftshift
5
+ from PIL import Image
6
+
7
+ def fourier_upscale(image, scale_factor=4):
8
+ upscaled_channels = []
9
+
10
+ for c in range(3): # For R, G, B channels
11
+ # Get single channel
12
+ channel = image[:, :, c]
13
+
14
+ # Forward FFT
15
+ F = fft2(channel)
16
+ F_shifted = fftshift(F)
17
+
18
+ # Zero-padding in frequency domain
19
+ h, w = channel.shape
20
+ new_h, new_w = h * scale_factor, w * scale_factor
21
+ F_padded = np.zeros((new_h, new_w), dtype=complex)
22
+
23
+ # Place the original spectrum in the center of the padded one
24
+ h_center, w_center = new_h // 2, new_w // 2
25
+ h_half, w_half = h // 2, w // 2
26
+ F_padded[h_center - h_half:h_center + h_half, w_center - w_half:w_center + w_half] = F_shifted
27
+
28
+ # Inverse FFT
29
+ F_unshifted = ifftshift(F_padded)
30
+ upscaled = np.real(ifft2(F_unshifted))
31
+
32
+ # ✅ Rescale intensities
33
+ upscaled *= scale_factor ** 2 # Rescale to preserve brightness
34
+
35
+ # Normalize to [0, 255]
36
+ upscaled = np.clip(upscaled, 0, 255)
37
+ upscaled_channels.append(upscaled.astype(np.uint8))
38
+
39
+ # Stack the 3 upscaled channels
40
+ upscaled_img = np.stack(upscaled_channels, axis=2)
41
+ return upscaled_img
models/lancros_interpolation.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+
4
+ def upsample_lancros(image_x,scale=4):
5
+ h, w = image_x.shape[:2]
6
+ new_w, new_h = int(w * scale), int(h * scale)
7
+
8
+ # Upsample using Lanczos interpolation
9
+ pred = cv2.resize(image_x, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4)
10
+ return pred
11
+
12
+ # def lanczos_kernel(x, a):
13
+ # """Lanczos kernel function."""
14
+ # if x == 0:
15
+ # return 1
16
+ # elif -a < x < a:
17
+ # return a * np.sinc(x) * np.sinc(x / a)
18
+ # else:
19
+ # return 0
20
+
21
+ # def upsample_lancros_2(image, scale=4, a=3):
22
+ # """
23
+ # Upsample an image using Lanczos interpolation.
24
+
25
+ # Parameters:
26
+ # image: numpy array (grayscale or RGB)
27
+ # scale: scaling factor (default 4x)
28
+ # a: size of Lanczos window (default 3)
29
+
30
+ # Returns:
31
+ # Upsampled image as numpy array.
32
+ # """
33
+ # if image.ndim == 2: # Grayscale
34
+ # h, w = image.shape
35
+ # channels = 1
36
+ # else: # RGB
37
+ # h, w, channels = image.shape
38
+
39
+ # new_h, new_w = int(h * scale), int(w * scale)
40
+ # output = np.zeros((new_h, new_w, channels)) if channels > 1 else np.zeros((new_h, new_w))
41
+
42
+ # for y_new in range(new_h):
43
+ # for x_new in range(new_w):
44
+ # print(y_new,x_new)
45
+ # # Map new pixel to original image space
46
+ # x_orig = x_new / scale
47
+ # y_orig = y_new / scale
48
+
49
+ # x0 = int(np.floor(x_orig))
50
+ # y0 = int(np.floor(y_orig))
51
+
52
+ # # Accumulators
53
+ # pixel = np.zeros(channels) if channels > 1 else 0.0
54
+ # norm = 0.0
55
+
56
+ # for j in range(y0 - a + 1, y0 + a + 1):
57
+ # for i in range(x0 - a + 1, x0 + a + 1):
58
+ # if 0 <= i < w and 0 <= j < h:
59
+ # wx = lanczos_kernel(x_orig - i, a)
60
+ # wy = lanczos_kernel(y_orig - j, a)
61
+ # weight = wx * wy
62
+ # if channels > 1:
63
+ # pixel += image[j, i] * weight
64
+ # else:
65
+ # pixel += image[j, i] * weight
66
+ # norm += weight
67
+
68
+ # if norm > 0:
69
+ # output[y_new, x_new] = pixel / norm
70
+
71
+ # if channels == 1:
72
+ # output = output.astype(image.dtype)
73
+ # else:
74
+ # output = output.clip(0, 255).astype(image.dtype)
75
+
76
+ # return output
models/nn_interpolation.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+
4
+ def nn_upscale(image, scale_factor=4):
5
+ """
6
+ Upscale image using Nearest Neighbor interpolation
7
+
8
+ Args:
9
+ image: Input image (numpy array or file path)
10
+ scale_factor: Scaling multiplier (default=4)
11
+
12
+ Returns:
13
+ Upscaled image as numpy array (uint8)
14
+ """
15
+ # Load image if path provided
16
+ if isinstance(image, str):
17
+ img = cv2.imread(image)
18
+ if img is None:
19
+ raise ValueError(f"Could not load image from {image}")
20
+ else:
21
+ img = image.copy()
22
+
23
+ # Get original dimensions
24
+ h, w = img.shape[:2]
25
+ new_h, new_w = h * scale_factor, w * scale_factor
26
+
27
+ # Create empty output image
28
+ if len(img.shape) == 3: # Color image
29
+ upscaled = np.zeros((new_h, new_w, 3), dtype=img.dtype)
30
+ else: # Grayscale
31
+ upscaled = np.zeros((new_h, new_w), dtype=img.dtype)
32
+
33
+ # Nearest Neighbor interpolation
34
+ for y in range(new_h):
35
+ for x in range(new_w):
36
+ orig_y = min(int(y / scale_factor), h - 1)
37
+ orig_x = min(int(x / scale_factor), w - 1)
38
+ upscaled[y, x] = img[orig_y, orig_x]
39
+
40
+ return upscaled
models/random_forest_sr.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from skimage import transform, util, color
3
+ from sklearn.ensemble import RandomForestRegressor
4
+ from skimage.util import view_as_windows
5
+ from PIL import Image
6
+
7
+ # CONFIGURATION
8
+ PATCH_SIZE = (3, 3)
9
+ STEP = 1
10
+ N_ESTIMATORS = 10
11
+ MAX_DEPTH = 10
12
+ SCALE_FACTOR = 2
13
+ SAMPLE_PATCHES = 10000 # Controls speed/accuracy trade-off
14
+
15
+ def extract_patches(img, patch_size, step):
16
+ patches = view_as_windows(img, patch_size, step)
17
+ h, w = patches.shape[:2]
18
+ return patches.reshape(h * w, -1)
19
+
20
+ def train_rf(X, y):
21
+ rf = RandomForestRegressor(
22
+ n_estimators=N_ESTIMATORS,
23
+ max_depth=MAX_DEPTH,
24
+ n_jobs=-1
25
+ )
26
+ rf.fit(X, y)
27
+ return rf
28
+
29
+ def predict_and_reconstruct(model, lr_img, patch_size, step, out_shape):
30
+ lr_patches = extract_patches(lr_img, patch_size, step)
31
+ preds = model.predict(lr_patches)
32
+
33
+ patch_h, patch_w = patch_size
34
+ img_h = (lr_img.shape[0] - patch_h) // step + 1
35
+ img_w = (lr_img.shape[1] - patch_w) // step + 1
36
+
37
+ result = np.zeros(out_shape)
38
+ weight = np.zeros(out_shape)
39
+
40
+ idx = 0
41
+ for i in range(img_h):
42
+ for j in range(img_w):
43
+ patch = preds[idx].reshape(patch_h, patch_w)
44
+ result[i*step:i*step+patch_h, j*step:j*step+patch_w] += patch
45
+ weight[i*step:i*step+patch_h, j*step:j*step+patch_w] += 1
46
+ idx += 1
47
+
48
+ weight[weight == 0] = 1
49
+ return result / weight
50
+
51
+ def random_forest_upscale(pil_img: Image.Image) -> Image.Image:
52
+ img = np.array(pil_img) / 255.0 # Normalize
53
+ if img.ndim == 2:
54
+ img = np.expand_dims(img, axis=-1)
55
+
56
+ hr_shape = (img.shape[0] * SCALE_FACTOR, img.shape[1] * SCALE_FACTOR)
57
+ sr_channels = []
58
+
59
+ for c in range(img.shape[2]):
60
+ channel = img[:, :, c]
61
+ hr_channel = transform.resize(channel, hr_shape, anti_aliasing=True)
62
+ lr_channel = transform.resize(hr_channel, (hr_shape[0] // SCALE_FACTOR, hr_shape[1] // SCALE_FACTOR), anti_aliasing=True)
63
+ lr_channel_up = transform.resize(lr_channel, hr_shape, anti_aliasing=True)
64
+
65
+ X = extract_patches(lr_channel_up, PATCH_SIZE, STEP)
66
+ y = extract_patches(hr_channel, PATCH_SIZE, STEP)
67
+
68
+ if X.shape[0] > SAMPLE_PATCHES:
69
+ idx = np.random.choice(X.shape[0], SAMPLE_PATCHES, replace=False)
70
+ X = X[idx]
71
+ y = y[idx]
72
+
73
+ rf_model = train_rf(X, y)
74
+ sr = predict_and_reconstruct(rf_model, lr_channel_up, PATCH_SIZE, STEP, hr_shape)
75
+ sr_channels.append(sr)
76
+
77
+ sr_image = np.stack(sr_channels, axis=-1)
78
+ sr_image = np.clip(sr_image * 255, 0, 255).astype(np.uint8)
79
+ return Image.fromarray(sr_image)
models/random_forest_sr1.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from skimage import transform, util
3
+ from sklearn.ensemble import RandomForestRegressor
4
+ from skimage.util import view_as_windows
5
+ from PIL import Image
6
+
7
+ # CONFIGURATION
8
+ PATCH_SIZE = (5, 5)
9
+ STEP = 1
10
+ N_ESTIMATORS = 50
11
+ MAX_DEPTH = 20
12
+ SCALE_FACTOR = 2
13
+
14
+ def extract_patches(img, patch_size, step):
15
+ patches = view_as_windows(img, patch_size, step)
16
+ h, w = patches.shape[:2]
17
+ return patches.reshape(h * w, -1)
18
+
19
+ def train_rf(X, y):
20
+ rf = RandomForestRegressor(n_estimators=N_ESTIMATORS, max_depth=MAX_DEPTH, n_jobs=-1)
21
+ rf.fit(X, y)
22
+ return rf
23
+
24
+ def predict_and_reconstruct(model, lr_img, patch_size, step, out_shape):
25
+ lr_patches = extract_patches(lr_img, patch_size, step)
26
+ preds = model.predict(lr_patches)
27
+
28
+ patch_h, patch_w = patch_size
29
+ img_h = (lr_img.shape[0] - patch_h) // step + 1
30
+ img_w = (lr_img.shape[1] - patch_w) // step + 1
31
+
32
+ result = np.zeros(out_shape)
33
+ weight = np.zeros(out_shape)
34
+
35
+ idx = 0
36
+ for i in range(img_h):
37
+ for j in range(img_w):
38
+ patch = preds[idx].reshape(patch_h, patch_w)
39
+ result[i*step:i*step+patch_h, j*step:j*step+patch_w] += patch
40
+ weight[i*step:i*step+patch_h, j*step:j*step+patch_w] += 1
41
+ idx += 1
42
+
43
+ weight[weight == 0] = 1
44
+ return result / weight
45
+
46
+ def random_forest_upscale(pil_img: Image.Image) -> Image.Image:
47
+ img = np.array(pil_img) / 255.0 # Normalize
48
+ if img.ndim == 2:
49
+ img = np.expand_dims(img, axis=-1)
50
+
51
+ hr_shape = (img.shape[0] * SCALE_FACTOR, img.shape[1] * SCALE_FACTOR)
52
+ sr_channels = []
53
+
54
+ for c in range(img.shape[2]):
55
+ channel = img[:, :, c]
56
+ hr_channel = transform.resize(channel, hr_shape)
57
+ lr_channel = transform.resize(hr_channel, (hr_shape[0] // SCALE_FACTOR, hr_shape[1] // SCALE_FACTOR))
58
+ lr_channel_up = transform.resize(lr_channel, hr_shape)
59
+
60
+ X = extract_patches(lr_channel_up, PATCH_SIZE, STEP)
61
+ y = extract_patches(hr_channel, PATCH_SIZE, STEP)
62
+ rf_model = train_rf(X, y)
63
+ sr = predict_and_reconstruct(rf_model, lr_channel_up, PATCH_SIZE, STEP, hr_shape)
64
+ sr_channels.append(sr)
65
+
66
+ sr_image = np.stack(sr_channels, axis=-1)
67
+ sr_image = np.clip(sr_image * 255, 0, 255).astype(np.uint8)
68
+ return Image.fromarray(sr_image)
models/rcan.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ from torchvision.io import read_image
5
+ from torchvision.utils import save_image
6
+ import os
7
+ from torchvision.transforms.functional import to_tensor, to_pil_image
8
+
9
+ # === RCAN MODULES ===
10
+
11
+ class CALayer(nn.Module):
12
+ def __init__(self, channels, reduction=16):
13
+ super().__init__()
14
+ self.avg_pool = nn.AdaptiveAvgPool2d(1)
15
+ self.fc = nn.Sequential(
16
+ nn.Conv2d(channels, channels // reduction, 1),
17
+ nn.ReLU(inplace=True),
18
+ nn.Conv2d(channels // reduction, channels, 1),
19
+ nn.Sigmoid()
20
+ )
21
+
22
+ def forward(self, x):
23
+ w = self.fc(self.avg_pool(x))
24
+ return x * w
25
+
26
+
27
+ class RCAB(nn.Module):
28
+ def __init__(self, channels):
29
+ super().__init__()
30
+ self.body = nn.Sequential(
31
+ nn.Conv2d(channels, channels, 3, padding=1),
32
+ nn.ReLU(inplace=True),
33
+ nn.Conv2d(channels, channels, 3, padding=1),
34
+ CALayer(channels)
35
+ )
36
+
37
+ def forward(self, x):
38
+ return x + self.body(x)
39
+
40
+
41
+ class ResidualGroup(nn.Module):
42
+ def __init__(self, channels, n_rcab):
43
+ super().__init__()
44
+ modules = [RCAB(channels) for _ in range(n_rcab)]
45
+ modules.append(nn.Conv2d(channels, channels, 3, padding=1))
46
+ self.body = nn.Sequential(*modules)
47
+
48
+ def forward(self, x):
49
+ return x + self.body(x)
50
+
51
+
52
+ class Upsampler(nn.Sequential):
53
+ def __init__(self, scale, channels):
54
+ m = []
55
+ for _ in range(int(torch.log2(torch.tensor(scale)))):
56
+ m.append(nn.Conv2d(channels, channels * 4, 3, padding=1))
57
+ m.append(nn.PixelShuffle(2))
58
+ super().__init__(*m)
59
+
60
+
61
+ class RCAN(nn.Module):
62
+ def __init__(self, in_channels=3, out_channels=3, n_feat=64, n_rg=10, n_rcab=20, scale=4):
63
+ super().__init__()
64
+ self.head = nn.Conv2d(in_channels, n_feat, 3, padding=1)
65
+ self.body = nn.Sequential(
66
+ *[ResidualGroup(n_feat, n_rcab) for _ in range(n_rg)],
67
+ nn.Conv2d(n_feat, n_feat, 3, padding=1)
68
+ )
69
+ self.upsample = Upsampler(scale, n_feat)
70
+ self.tail = nn.Conv2d(n_feat, out_channels, 3, padding=1)
71
+
72
+ def forward(self, x):
73
+ x = self.head(x)
74
+ res = self.body(x)
75
+ x = x + res
76
+ x = self.upsample(x)
77
+ return self.tail(x)
78
+
79
+ # === INFERENCE ===
80
+
81
+ def rcan_upscale(lr_img_pil, model_path="weights/rcan_epoch_20.pth", device='cpu'):
82
+ """
83
+ Super resolves a low-resolution PIL image using the RCAN model.
84
+
85
+ Args:
86
+ lr_img_pil (PIL.Image): Low-resolution input image.
87
+ model_path (str): Path to the model weights.
88
+ device (str): 'cuda' or 'cpu'.
89
+
90
+ Returns:
91
+ PIL.Image: High-resolution output image.
92
+ """
93
+ # Load model
94
+ device = torch.device(device if torch.cuda.is_available() else 'cpu')
95
+ model = RCAN(scale=4)
96
+ model.load_state_dict(torch.load(model_path, map_location=device))
97
+ model.to(device).eval()
98
+
99
+ # Convert PIL image to normalized tensor
100
+ lr_tensor = to_tensor(lr_img_pil).unsqueeze(0).to(device) # Add batch dim
101
+
102
+ # Inference
103
+ with torch.no_grad():
104
+ sr_tensor = model(lr_tensor).squeeze(0).clamp(0, 1).cpu() # Remove batch
105
+
106
+ # Convert tensor back to PIL image
107
+ sr_img_pil = to_pil_image(sr_tensor)
108
+
109
+ return sr_img_pil
110
+
111
+
models/spline_interpolation.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+
4
+ def spline_upscale(image, scale_factor=4):
5
+ """
6
+ Upscale image using Bicubic spline interpolation
7
+
8
+ Args:
9
+ image: Input image (numpy array or file path)
10
+ scale_factor: Scaling multiplier (default=4)
11
+
12
+ Returns:
13
+ Upscaled image as numpy array (uint8)
14
+ """
15
+ # Load image if path provided
16
+ if isinstance(image, str):
17
+ img = cv2.imread(image)
18
+ if img is None:
19
+ raise ValueError(f"Could not load image from {image}")
20
+ else:
21
+ img = image.copy()
22
+
23
+ # Get original dimensions
24
+ h, w = img.shape[:2]
25
+
26
+ # Calculate new dimensions
27
+ new_w = int(w * scale_factor)
28
+ new_h = int(h * scale_factor)
29
+
30
+ # Perform bicubic interpolation
31
+ upscaled = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_CUBIC)
32
+
33
+ return upscaled
models/sr_gan.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from collections import OrderedDict
4
+ import numpy as np
5
+ from PIL import Image,ImageOps
6
+
7
+ class ResidualBlock(nn.Module):
8
+ def __init__(self, channels):
9
+ super().__init__()
10
+ self.block = nn.Sequential(
11
+ nn.Conv2d(channels, channels, 3, 1, 1),
12
+ nn.BatchNorm2d(channels),
13
+ nn.PReLU(),
14
+ nn.Conv2d(channels, channels, 3, 1, 1),
15
+ nn.BatchNorm2d(channels)
16
+ )
17
+
18
+ def forward(self, x):
19
+ return x + self.block(x)
20
+
21
+ class Generator(nn.Module):
22
+ def __init__(self, in_channels=3, num_res_blocks=16):
23
+ super().__init__()
24
+
25
+ self.conv1 = nn.Sequential(
26
+ nn.Conv2d(in_channels, 64, kernel_size=9, stride=1, padding=4),
27
+ nn.PReLU()
28
+ )
29
+
30
+ self.res_blocks = nn.Sequential(*[ResidualBlock(64) for _ in range(num_res_blocks)])
31
+
32
+ self.conv2 = nn.Sequential(
33
+ nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
34
+ nn.BatchNorm2d(64)
35
+ )
36
+
37
+ # Upsampling by 2x → 256x256, then another 2x → 512x512
38
+ self.upsample = nn.Sequential(
39
+ nn.Conv2d(64, 256, kernel_size=3, stride=1, padding=1),
40
+ nn.PixelShuffle(2), # 128 → 256
41
+ nn.PReLU(),
42
+
43
+ nn.Conv2d(64, 256, kernel_size=3, stride=1, padding=1),
44
+ nn.PixelShuffle(2), # 256 → 512
45
+ nn.PReLU()
46
+ )
47
+
48
+ self.conv3 = nn.Conv2d(64, in_channels, kernel_size=9, stride=1, padding=4)
49
+
50
+ def forward(self, x):
51
+ initial = self.conv1(x)
52
+ res = self.res_blocks(initial)
53
+ res = self.conv2(res)
54
+ out = initial + res
55
+ out = self.upsample(out)
56
+ out = self.conv3(out)
57
+ return torch.clamp(out, 0.0, 1.0) # to keep output in [0,1]
58
+
59
+
60
+ def srgan_upscale(image):
61
+ image = Image.fromarray(image)
62
+ target_size = (128, 128)
63
+ pad_color=(0, 0, 0)
64
+ ImageOps.pad(image, target_size, method=Image.BICUBIC, color=pad_color)
65
+ image = np.array(image)
66
+
67
+ image = image/255
68
+ image = torch.from_numpy(image).float().unsqueeze(dim=0).permute(0,3,1,2)
69
+
70
+ model = Generator()
71
+ checkpoint = torch.load("weights/model_srgan.pth", map_location=torch.device('cpu')) # or 'cuda' if using GPU
72
+ state_dict = checkpoint['G_state_dict']
73
+
74
+ new_state_dict = OrderedDict()
75
+ for k, v in state_dict.items():
76
+ new_key = k.replace("module.", "") # Remove "module." from each key
77
+ new_state_dict[new_key] = v
78
+
79
+ model.load_state_dict(new_state_dict)
80
+ model.eval()
81
+
82
+ with torch.no_grad():
83
+ output = model(image)
84
+
85
+ output = output.squeeze(0).permute(1, 2, 0).cpu().numpy()
86
+ output = (output * 255.0).clip(0, 255).astype("uint8")
87
+ return output
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ Pillow
3
+ numpy
4
+ opencv-python
5
+ scipy
6
+ scikit-learn
7
+ torch
8
+ scikit-image
9
+ torchvision
sample_images/0001.png ADDED
sample_images/0002.png ADDED
sample_images/0003.png ADDED
sample_images/0004.png ADDED
sample_images/0006.png ADDED
sample_images/0007.png ADDED
sample_images/0010.png ADDED
sample_images/0012.png ADDED
sample_images/0019.png ADDED
sample_images/0021.png ADDED
sample_images/0024.png ADDED
sample_images/0055.png ADDED
sample_images/0064.png ADDED
sample_images/0068.png ADDED
sample_images/0086.png ADDED
sample_images/0097.png ADDED
sample_images/0171.png ADDED
sample_images/0172.png ADDED
weights/model_auto_2.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:78f0ca09d2f705b77b835d049c4492a07a89a28dcc59786452bc5406c26070fe
3
+ size 2090274
weights/model_cnn.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2f55c32baba0df6255a4527218100929984b39f444fd6419953ee9e1ac3ae8df
3
+ size 158582
weights/model_espcn.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:844a4dc4e53d3db576b00d8ef5fa07fcdd26bbb1119738135f9fb04845f06df4
3
+ size 454114
weights/model_srgan.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4d056e0bf694b2e48e7aac4513fe2178b781865881b4f5dc0df5eda6e6f5f8e5
3
+ size 81484154
weights/rcan_epoch_20.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1efb22fe861cd418170c0b9527d968dc0f11aee46fa20fd3a58e57c8016418dc
3
+ size 63015800