{"id":19927338,"url":"https://github.com/juanse77/super-resolution","last_synced_at":"2025-08-10T16:37:00.139Z","repository":{"id":112257215,"uuid":"337264801","full_name":"juanse77/Super-Resolution","owner":"juanse77","description":"Image super resolution implementation","archived":false,"fork":false,"pushed_at":"2021-02-13T12:55:44.000Z","size":94701,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-01T10:46:05.536Z","etag":null,"topics":["example-code","images","pytorch","resolutions"],"latest_commit_sha":null,"homepage":"","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/juanse77.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-09T02:02:34.000Z","updated_at":"2024-07-18T19:32:22.000Z","dependencies_parsed_at":"2023-03-16T21:45:42.705Z","dependency_job_id":null,"html_url":"https://github.com/juanse77/Super-Resolution","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/juanse77/Super-Resolution","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juanse77%2FSuper-Resolution","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juanse77%2FSuper-Resolution/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juanse77%2FSuper-Resolution/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juanse77%2FSuper-Resolution/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juanse77","download_url":"https://codeload.github.com/juanse77/Super-Resolution/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juanse77%2FSuper-Resolution/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269754070,"owners_count":24470514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["example-code","images","pytorch","resolutions"],"created_at":"2024-11-12T22:33:09.853Z","updated_at":"2025-08-10T16:37:00.045Z","avatar_url":"https://github.com/juanse77.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Super-resolución 2x:\n## Modelado y Entranamiento:\n\nEjercicio de curso para la asignatura de Computación Inteligente perteneciente al Máster Universitario en Sistemas Inteligentes y Aplicaciones Numéricas para la Ingeniería (MUSIANI) en el curso 2020/21, realizado por Juan Sebastián Ramírez Artiles.\n\nEl ejercicio consiste en implementar un método de superresolución en imágenes basado en el método VDSR descrito en el artículo [Superresolución de una sola imagen mediante el aprendizaje profundo](https://es.mathworks.com/help/images/single-image-super-resolution-using-deep-learning.html). El dataset usado fue el [ffhq-dataset](https://drive.google.com/drive/folders/1u2xu7bSrWxrbUxk-dT-UvEJq8IjdmNTP) de libre descarga. Las imágenes usadas son las del conjunto 64000. En el dataset original las imágenes de alta resolución son de 1024x1024, y de 128x128 para las de baja resolución. Para la realización de este ejercicio se ha reducido las imágenes de alta resolución a 256x256 de modo que serán las que se usarán como etiquetas de la red. Con lo que se realizará un escalado de dos aumentos, de 128x128 a 256x256.\n\n\n![png](portada.png)\n\n\n```python\n%matplotlib inline\n%config InlineBackend.figure_format = 'retina'\nimport matplotlib.pyplot as plt\nimport torch\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import DataLoader, Dataset\nimport torch.nn.functional as F\nimport numpy as np\nimport torch.nn as nn\nimport torch.optim as optim\nfrom os import listdir\nfrom os.path import join\nfrom PIL import Image\n```\n\nLas imágenes se cargan de cuatro directorios. Las imágenes reales se situan en train_y y valid_y, mientras que las imágenes a ampliar se encuentran en los directorios train_x y valid_x.\n\nEl programa se ejecutó en una máquina con un procesador Intel Core i7-7700HQ a 2.80GHz con una tarjeta de vídeo NVIDIA GeForce GTX 1050 de 4GB de memoria dedicada y con 32 GB de memoria RAM.\n\n![](Equipo.JPG)\n\n\n```python\nngpu = 1\nbeta1 = 0.5\nlr = 0.005\nbs = 150\nepochs = 50\n\npath_train_x = \"faces/train_x/x8\"\npath_train_y = \"faces/train_y/x4\"\n\npath_valid_x = \"faces/valid_x/x8\"\npath_valid_y = \"faces/valid_y/x4\"\n```\n\nEl entrenamiento del modelo se realizó únicamente en la capa de luminancia mientras que las capas de crominancia se escalaron por el método tradicional. El resultado final no se verá perjudicado en exceso, ya que las crominancias influyen mínimamente en la calidad del contorno. Para lograr este objetivo se hizo necesario convertir las imágenes de RGB a YCbCr.\n\nPara la generación del dataset y las transformaciones del mismo se usó como modelo el ejemplo publicado en la web de pytorch [Writting custom datasets, dataloaders and transforms](https://pytorch.org/tutorials/beginner/data_loading_tutorial.html).\n\n\n```python\nclass FacesDataset(Dataset):\n    \n    def __init__(self, root_dir_x, root_dir_y, transform=None):\n        self.x_files = listdir(root_dir_x)\n        self.y_files = listdir(root_dir_y)\n        \n        assert  len(self.x_files) == len(self.x_files)\n        \n        self.dir_x = root_dir_x\n        self.dir_y = root_dir_y\n        \n        self.transform = transform\n\n    def __len__(self):\n        return len(self.x_files)\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n                \n        img_x = Image.open(join(self.dir_x, self.x_files[idx]))\n        img_y = Image.open(join(self.dir_y, self.y_files[idx]))\n                \n        img_set = {'img_x': img_x, 'img_y': img_y}\n        \n        if self.transform:\n            img_set = self.transform(img_set)\n\n        return img_set\n```\n\n\n```python\nclass PilToYCbCr(object):\n    def __call__(self, img_set):\n        imgs = []\n        \n        for _, img in img_set.items():            \n            imgs.append(img.convert('YCbCr'))\n            \n        return {'img_x': imgs[0], 'img_y': imgs[1]}\n```\n\n\n```python\nclass ToTensor(object):\n    def __call__(self, img_set):\n        \n        imgs = []\n                \n        for _, img in img_set.items():\n            tr = transforms.ToTensor()\n            imgs.append(tr(img))\n        \n        return { 'img_x': imgs[0], 'img_y': imgs[1] }\n```\n\n\n```python\nclass Normalize(object):\n    def __call__(self, img_set):\n        imgs = []\n        mean = torch.Tensor([0.5])\n        std = 0.5\n        \n        for _, img in img_set.items():\n            \n            img = (img - mean.expand_as(img)) / std\n            imgs.append(img)\n        \n        return {'img_x': imgs[0], 'img_y': imgs[1]}\n```\n\n\n```python\ntransform = transforms.Compose([\n    PilToYCbCr(),\n    ToTensor(),\n    Normalize()\n])\n\nimgs_train = FacesDataset(path_train_x, path_train_y, transform = transform)\nimgs_valid = FacesDataset(path_valid_x, path_valid_y, transform = transform)\n```\n\n\n```python\nimgs_train_dl = DataLoader(imgs_train, batch_size = bs, shuffle = True)\nimgs_valid_dl = DataLoader(imgs_valid, batch_size = bs, shuffle = True)\n```\n\nEl dataset se dividió en 900 imágenes para entrenamiento y 100 imágenes para validación.\n\n\n```python\nprint(len(imgs_train))\nprint(len(imgs_valid))\n```\n\n    900\n    100\n    \n\n\n```python\ndevice = torch.device(\"cuda:0\" if (torch.cuda.is_available() and ngpu \u003e 0) else \"cpu\")\n```\n\nSe probaron una variedad de modelos diferentes. Se usaron kernels de 3x3, de 5x5 y de 7x7, siendo estos últimos los que mejor resultados dieron. También se probaron diferentes configuraciones de red, añadiendo capas convolutivas y modificando las funciones de activación. Esta configuración resultó la más adecuada.\n\nEn el proceso de entrenamiento se usaron gran variedad de combinaciones de tamaños de batches y de cantidad de épocas. Se empezó con tamaños de batch de 8, se fue subiendo hasta 150, dejando todavía 900 MB de espacio en GPU. En cuanto al número de épocas, se empezó con 10 iteraciones y se fue subiendo hasta la cantidad de 80. No obstante, al variar el learning rate se aceleró la convergencia, con lo que finalmente se bajó a 50 épocas.\n\n\n```python\nclass SuperResolution(nn.Module):\n    def __init__(self):\n        super().__init__()\n        \n        self.conv1 = nn.Conv2d(1, 8, kernel_size = 7, padding = 3)\n        self.conv2 = nn.Conv2d(8, 16, kernel_size = 7, padding = 3)\n        self.conv3 = nn.Conv2d(16, 8, kernel_size = 7, padding = 3)\n        self.conv4 = nn.Conv2d(8, 1, kernel_size = 7, padding = 3)\n        \n        self.upsample = nn.Upsample(scale_factor = 2)\n\n    def forward(self, xb):\n        \n        xb = self.upsample(xb)\n        xb = torch.tanh(self.conv1(xb))\n        xb = torch.tanh(self.conv2(xb))\n        xb = torch.tanh(self.conv3(xb))\n        \n        return torch.tanh(self.conv4(xb))\n```\n\n\n```python\ndef preprocess(x, y):\n    return x.to(device), y.to(device)\n```\n\n\n```python\ndef get_model():\n    model = SuperResolution().to(device)\n    return model, optim.SGD(model.parameters(), lr=lr, momentum=0.9)\n\nloss_func = nn.MSELoss(reduction='mean')\n```\n\n\n```python\nclass WrappedDataLoader:\n    def __init__(self, dl, func):\n        self.dl = dl\n        self.func = func\n    \n    def __len__(self):\n        return len(self.dl)\n\n    def __iter__(self):\n        for sample_batch in imgs_train_dl:\n            yield (self.func(sample_batch['img_x'][:,0].unsqueeze(-3), sample_batch['img_y'][:,0].unsqueeze(-3)))        \n```\n\n\n```python\ndef loss_batch(model, loss_func, xb, yb, opt=None):\n    loss = loss_func(model(xb), yb)\n    if opt is not None:\n        loss.backward()\n        opt.step()\n        opt.zero_grad()\n    \n    return loss.item(), len(xb)\n```\n\n\n```python\ndef fit(epochs, model, loss_func, opt, train_dl, valid_dl, val_losses):\n    for epoch in range(epochs):\n        model.train()\n        for xb, yb in train_dl:\n            loss_batch(model, loss_func, xb, yb, opt)\n\n        model.eval()\n        with torch.no_grad():\n            losses, nums = zip(\n                *[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl]\n            )\n        val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)\n        val_losses.append(val_loss)\n        \n        print(epoch, val_loss)\n```\n\n\n```python\ntrain_dl = WrappedDataLoader(imgs_train, preprocess)\nvalid_dl = WrappedDataLoader(imgs_valid, preprocess)\n\nval_losses = []\n\nmodel, opt = get_model()\nfit(epochs, model, loss_func, opt, train_dl, valid_dl, val_losses)\n```\n\n    0 0.06483114262421925\n    1 0.035555280124147735\n    2 0.0427752248942852\n    3 0.03147281768421332\n    ...\n    ...\n    47 0.010295956550786892\n    48 0.010223751422017813\n    49 0.010162690499176582\n    \n\nSe puede observar en el gráfico que la red aprende bien.\n\n\n```python\nplt.plot(val_losses)\nplt.show()\n```\n\n\n![png](output_24_0.png)\n\n\nPara finalizar, salvo el modelo para poder usarlo en el notebook [SR_Restore_Faces_Model.ipynb](SR_Restore_Faces_Model.md) que se encargará de escalar las 100 imágenes de validación. - Modelo entrenado [SR_model_Faces_5.0.ml](SR_model_Faces_5.0.ml) -\n\n\n```python\ntorch.save(model, \"SR_model_Faces_5.0.ml\")\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuanse77%2Fsuper-resolution","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuanse77%2Fsuper-resolution","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuanse77%2Fsuper-resolution/lists"}