Processo de uso do BasicSR

        Como tenho estudado recentemente vários modelos de super-resolução BasicSR, muitos problemas surgiram durante o teste de vários experimentos. Aqui estão algumas soluções para esses problemas. Por exemplo, como o problema que encontrei desta vez, como devemos lidar com isso quando os dados da imagem de entrada são particularmente pequenos. Este artigo fornece apenas uma descrição parcial da instalação, uso e implementação de alguma atenção ao uso do BasicSR para uso e comparação de modelos.

 1. Instalação

O tutorial de instalação é fornecido no documento do Sr. Wang Xintao. Como muitos alunos podem não gostar de baixar o documento oficial para lê-lo, reescreverei o processo de instalação aqui. E fale sobre as vantagens e desvantagens desses dois métodos de instalação.

BasicSR é desenvolvido com base na estrutura de aprendizado profundo pytorch. Portanto, recomenda-se que aqueles alunos que viram apenas o tensorflow dêem uma olhada na documentação da interface API do pytorch ( documentação do PyTorch — documentação do PyTorch 1.13 ) Pode ser mais conveniente ler sua documentação. Se quiser rodar na GPU, você precisa configurar previamente o ambiente CUDA correspondente em seu computador. Mas como meu computador não possui GPU, utilizo o ambiente que foi configurado no servidor. Então você pode pesquisar no Google o tutorial para configurar o CUDA, não é problemático aqui.

Instalação de código clone local (recomendado para alunos que desejam testar vários modelos localmente e desejam estudar e modificar o código-fonte)

1. Clone o projeto:
        git clone https://github.com/XPixelGroup/BasicSR.git

2. Instale pacotes de dependência:

        Os requisitos de versão dos pacotes dependentes deste ambiente foram compilados em requisitos.txt.

cd BasicSR
pip install -r requirements.txt

3. Instale o BasicSR no diretório raiz do BasicSR:

python setup.py develop

Se desejar especificar o caminho CUDA durante a instalação, você pode usar o seguinte comando: (Claro, você também pode especificá-lo manualmente enquanto o programa está em execução, portanto, esta etapa é opcional)

CUDA_HOME=/usr/local/cuda \
CUDNN_INCLUDE_DIR=/usr/local/cuda \
CUDNN_LIB_DIR=/usr/local/cuda \
python setup.py develop

Instalar via pip

Para instalar o BasicSR usando pip, basta executar o seguinte comando no terminal:

pip install basicsr

Se quiser especificar o caminho CUDA durante a instalação, você pode usar o seguinte comando:

CUDA_HOME=/usr/local/cuda \
CUDNN_INCLUDE_DIR=/usr/local/cuda \
CUDNN_LIB_DIR=/usr/local/cuda \
pip install basicsr

Verifique se o BasicSR foi instalado com sucesso

 Se nenhum erro for relatado neste momento, significa que o BasicSR foi instalado com sucesso e agora você pode desenvolver com base no BasicSR ~~~

 2. Interpretação básica do catálogo

Esta parte escrita por Wang Xintao é bastante detalhada. Aqui descreverei o que todos devem prestar atenção ao usá-lo com base no meu entendimento e uso nos últimos dias.

Vermelho indica arquivos diretamente relacionados à execução de experimentos, ou seja, os arquivos com os quais costumamos lidar mais;

Azul indica outros arquivos de código fortemente relacionados ao BasicSR;

Preto representa o perfil.

No repositório BasicSR, o código principal está na pasta basicsr. Esta parte contém principalmente arquivos de código comumente usados ​​em modelos de aprendizagem profunda, como estrutura de rede, função de perda e carregamento de dados, etc.

Entre eles, o vermelho indica os arquivos que modificamos principalmente durante o desenvolvimento.

 

(Deve ser observado aqui, por quê? O arquivo vermelho mencionado acima é de fato um local onde podemos modificá-lo, mas há requisitos rígidos em seu código para o tamanho da imagem. Portanto, você deve ter cuidado ao modificar! Portanto, seja certifique-se de modificá-lo . Tenha cuidado! Portanto, tenha cuidado ao modificar! )

Como alguns scripts também são necessários no projeto e desenvolvimento de algoritmos, como pré-processamento de dados, cálculo de indicadores, etc., os arquivos relevantes estão localizados em scripts e o diretório é o seguinte

 Os scripts acima são basicamente uma preparação de dados, que pode baixar dados e muitos modelos de dados. Mas um problema é que alguns dos modelos de dados pré-treinados podem não estar acessíveis na China. Você precisa pular o muro, então precisa encontrar outros recursos.

No entanto, pode haver muitos problemas neste script que não podem resolver os problemas com os quais realmente queremos lidar, então podemos escrever nós mesmos os scripts de dados no estágio de processamento.

3. Pré-processamento de dados

A imagem que usei desta vez era muito pequena, então houve um problema de tamanho quando usei seu modelo de super-resolução ESRGAN diretamente aqui. Então aqui só posso redimensionar meus dados atuais de tamanho pequeno.

Alguns alunos podem dizer: O redimensionamento não perderá a precisão da imagem? Sim. Eu também tive esse problema na época e depois experimentei. Neste ponto, mencionaremos brevemente como conduzi o teste. Você pode consultar minhas ideias. Ampliei a foto dele e usei

        1. Bicúbico (interpolação bicúbica) dimensiona a imagem (por que usar a diferença bicúbica? Para sua conveniência, listei suas vantagens e desvantagens abaixo, incluindo seu algoritmo de interpolação bicúbica)

        2. Use resize() em cv2 para dimensionar a imagem ampliada em 1

        3. Julgue o efeito comparando os valores de PSNR e SSIM.

   

 

# 双三次插值算法    img:图像,dstH:目标图像的高 dstW: 目的图像的宽
def BiCubic_interpolation(img, dstH, dstW):
    scrH, scrW, _= img.shape
    retimg = np.zeros((dstH, dstW, 3), dtype=np.uint8)
    for i in range(dstH):
        for j in range(dstW):
            scrx = i*(scrH/dstH)
            scry = j*(scrW/dstW)
            x = math.floor(scrx)    # floor向下取整
            y = math.floor(scry)
            u = scrx-x
            v = scry-y
            tmp = 0
            for ii in range(-1, 2):
                for jj in range(-1, 2):
                    if x+ii < 0 or y+jj < 0 or x+ii >= scrH or y+jj >= scrW:
                        continue
                    tmp += img[x+ii, y+jj] * BiBubic(ii-u) * BiBubic(jj-v)
            retimg[i, j] = np.clip(tmp, 0, 255)
    return retimg

Aqui mencionamos brevemente o que funciona melhor e como eu fiz isso. Como o Sr. Wang Xintao estava escrevendo esta parte para pessoas ou outras imagens de alta definição, é aceitável usar as imagens fornecidas em seus exemplos aqui ou imagens comuns. Porque o arquivo de configuração que ele forneceu em seu modelo de configuração ESR usa 128 como tamanho de corte da imagem. Por que ele faria isso? As imagens que ele fornece ou as imagens que usamos diariamente para testes geralmente têm uma resolução relativamente grande. O conjunto de dados fornecido por este modelo é 2048x1024! Encontrou o problema? Sim, os dados fornecidos são relativamente grandes. Então isso nos leva ao motivo pelo qual ele quis cortá-lo. Quando treinamos, muitas vezes não precisamos ser tão grandes (os mais comuns são patches de treinamento de 128x128 ou 192x192). Portanto, podemos primeiro cortar a imagem 2K em blocos de subimagem de 480x480 com sobreposição. E então, o dataloader corta aleatoriamente um patch de treinamento de 128×128 (ESR) ou 192×192 (EDSR) deste bloco de subimagem de 480×480.

Você pode ter o mesmo entendimento que eu na descrição acima. Não há como entender completamente o processo dele sem conduzir o experimento. Então deixe-me descrever em vernáculo o que ele realmente quer expressar. Seu significado básico é, na verdade, expressar que quando inserimos uma imagem relativamente grande, para não sobrecarregar muito a memória de vídeo, o que devemos fazer? Podemos primeiro cortar a imagem dela em múltiplas imagens, como as peças de um quebra-cabeça. Lidamos apenas com uma peça de cada vez. Quando cortamos, queremos dizer que o comprimento e a largura da imagem podem ser diferentes, certo? ? Então, como lidamos com esse problema? É a sobreposição mencionada acima. Portanto, não importa se eles se sobrepõem. Portanto, não importa se ele divide seu gt_size por toda a imagem. Então usamos a mesma estratégia para suas subfotos.

Na última parte falarei sobre o problema que estou enfrentando agora, que é a situação que encontramos no início, e por falar nisso, vou avisar a todos. Se a imagem que inserimos não tiver um tamanho de imagem comum na vida real, mas estiver lidando com uma imagem muito pequena que foi segmentada, o tamanho será de apenas alguns KB, os pixels serão apenas 72x28, 36x63, etc. Portanto, não podemos usar diretamente o modelo de rede ESRGAN em seu modelo, na verdade, podemos ver pelo seu nome que o exemplo que ele deu usa um conjunto de dados de 2K para operação. Então, como lidamos com esse problema? Quando estava fazendo essa parte, fiz com a mentalidade de mover montanhas! De qualquer forma, se meus dados não estiverem errados, não vou alterá-los. Vou mudar o código dele e depois irei para o diretório basicsr e mudarei o modelo dele. Para me adaptar ao meu tamanho, mudei muitas coisas . Mas mesmo que algumas das alterações tenham sido bem sucedidas, ainda havia algumas imagens extremamente pequenas no final e eu ainda não consegui obter a minha imagem mais pequena. Então me perguntei se poderia excluir a função de corte dela! Então corri novamente, e o resultado foi óbvio e falhou (embora eu seja relativamente bom nisso, desta vez na verdade o TODO do framework dele também dizia que a escrita dele naquela parte não era muito boa, então não tive como fazer isso no curto prazo naquele momento. Dê a ele uma maneira melhor de mudar isso). Como resultado, deitei-me muito e meu estado mental não era muito bom. Finalmente, veio um ponto de viragem———————— Finalmente, meu mentor me deu algumas ideias e de repente me iluminei.

Solução para imagens minúsculas: Na verdade, entrei em um mal-entendido no início, ou seja, tenho pensado em mudar as coisas dos outros para se adequarem às minhas, mas na verdade é mais fácil resolvê-lo mudando meu pensamento e mudando nosso conjunto de dados . Então modifiquei nossos dados de imagem para o tamanho de uma imagem normal, que é maior que o gt_size no arquivo de configuração ESRGAN que ele forneceu. (Este gt_size realmente não deve ser alterado casualmente, porque sua entrada e convolução são codificadas em py e não são modificadas dinamicamente, então é muito fácil ter problemas em uma das etapas.) Então, quando nossa imagem atingir o tamanho normal Você pode prosseguir para a próxima etapa. Você está se perguntando se ampliar a imagem afetará a precisão da imagem? Não tem problema, foi explicado acima. Assim, todos podem usá-lo com confiança depois de passarem no teste PSNR ou SSIM. A partir desta parte, nossa fase de preparação de dados está oficialmente concluída.

Aqui estão seus scripts oficiais para processar subarquivos e processar pequenas imagens:



import cv2
import numpy as np
import os
import sys
from multiprocessing import Pool
from os import path as osp
from tqdm import tqdm

from basicsr.utils import scandir


def main():
    """A multi-thread tool to crop large images to sub-images for faster IO.
    It is used for DIV2K dataset.
    Args:
        opt (dict): Configuration dict. It contains:
        n_thread (int): Thread number.
        compression_level (int):  CV_IMWRITE_PNG_COMPRESSION from 0 to 9. A higher value means a smaller size and
            longer compression time. Use 0 for faster CPU decompression. Default: 3, same in cv2.
        input_folder (str): Path to the input folder.
        save_folder (str): Path to save folder.
        crop_size (int): Crop size.
        step (int): Step for overlapped sliding window.
        thresh_size (int): Threshold size. Patches whose size is lower than thresh_size will be dropped.
    Usage:
        For each folder, run this script.
        Typically, there are four folders to be processed for DIV2K dataset.
            * DIV2K_train_HR
            * DIV2K_train_LR_bicubic/X2
            * DIV2K_train_LR_bicubic/X3
            * DIV2K_train_LR_bicubic/X4
        After process, each sub_folder should have the same number of subimages.
        Remember to modify opt configurations according to your settings.
    """

    opt = {}
    opt['n_thread'] = 10
    opt['compression_level'] = 3

    # HR images
    opt['input_folder'] = '../../datasets/example/BSDS100'    # 数据图片的源文件
    opt['save_folder'] = '../../datasets/example/BSDS100_sub'  # 数据图片的压缩之后的文件夹
    opt['crop_size'] = 120
    opt['step'] = 240
    opt['thresh_size'] = 1
    extract_subimages(opt)




def extract_subimages(opt):
    """Crop images to subimages.
    Args:
        opt (dict): Configuration dict. It contains:
        input_folder (str): Path to the input folder.
        save_folder (str): Path to save folder.
        n_thread (int): Thread number.
    """
    input_folder = opt['input_folder']
    save_folder = opt['save_folder']
    if not osp.exists(save_folder):
        os.makedirs(save_folder)
        print(f'mkdir {save_folder} ...')
    else:
        print(f'Folder {save_folder} already exists. Exit.')
        sys.exit(1)

    img_list = list(scandir(input_folder, full_path=True))

    pbar = tqdm(total=len(img_list), unit='image', desc='Extract')
    pool = Pool(opt['n_thread'])
    for path in img_list:
        pool.apply_async(worker, args=(path, opt), callback=lambda arg: pbar.update(1))
    pool.close()
    pool.join()
    pbar.close()
    print('All processes done.')


def worker(path, opt):
    """Worker for each process.
    Args:
        path (str): Image path.
        opt (dict): Configuration dict. It contains:
        crop_size (int): Crop size.
        step (int): Step for overlapped sliding window.
        thresh_size (int): Threshold size. Patches whose size is lower than thresh_size will be dropped.
        save_folder (str): Path to save folder.
        compression_level (int): for cv2.IMWRITE_PNG_COMPRESSION.
    Returns:
        process_info (str): Process information displayed in progress bar.
    """
    crop_size = opt['crop_size']
    step = opt['step']
    thresh_size = opt['thresh_size']
    img_name, extension = osp.splitext(osp.basename(path))

    # remove the x2, x3, x4 and x8 in the filename for DIV2K
    img_name = img_name.replace('x2', '').replace('x3', '').replace('x4', '').replace('x8', '')

    img = cv2.imread(path, cv2.IMREAD_UNCHANGED)

    h, w = img.shape[0:2]
    h_space = np.arange(0, h - crop_size + 1, step)
    if h - (h_space[-1] + crop_size) > thresh_size:
        h_space = np.append(h_space, h - crop_size)
    w_space = np.arange(0, w - crop_size + 1, step)
    if w - (w_space[-1] + crop_size) > thresh_size:
        w_space = np.append(w_space, w - crop_size)

    index = 0
    for x in h_space:
        for y in w_space:
            index += 1
            cropped_img = img[x:x + crop_size, y:y + crop_size, ...]
            cropped_img = np.ascontiguousarray(cropped_img)
            cv2.imwrite(
                osp.join(opt['save_folder'], f'{img_name}_s{index:03d}{extension}'), cropped_img,
                [cv2.IMWRITE_PNG_COMPRESSION, opt['compression_level']])
    process_info = f'Processing {img_name} ...'
    return process_info


if __name__ == '__main__':
    main()

Em seguida, forneça o subscript para processamento de arquivos pequenos:

import os
from os import path as osp
import cv2


def scandir(dir_path, suffix=None, recursive=False, full_path=False):
    """Scan a directory to find the interested files.

    Args:
        dir_path (str): Path of the directory.
        suffix (str | tuple(str), optional): File suffix that we are
            interested in. Default: None.
        recursive (bool, optional): If set to True, recursively scan the
            directory. Default: False.
        full_path (bool, optional): If set to True, include the dir_path.
            Default: False.

    Returns:
        A generator for all the interested files with relative paths.
    """

    if (suffix is not None) and not isinstance(suffix, (str, tuple)):
        raise TypeError('"suffix" must be a string or tuple of strings')

    root = dir_path

    def _scandir(dir_path, suffix, recursive):
        for entry in os.scandir(dir_path):
            if not entry.name.startswith('.') and entry.is_file():
                if full_path:
                    return_path = entry.path
                else:
                    return_path = osp.relpath(entry.path, root)

                if suffix is None:
                    yield return_path
                elif return_path.endswith(suffix):
                    yield return_path
            else:
                if recursive:
                    yield from _scandir(entry.path, suffix=suffix, recursive=recursive)
                else:
                    continue

    return _scandir(dir_path, suffix=suffix, recursive=recursive )
pnglist = list(scandir("../../datasets/example/bai_hr", full_path=True))
print(pnglist)

#读取原始图像
for i in pnglist:

    img = cv2.imread(i,1)
    x, y, p = img.shape[0:3]

    #图像向下取样
    r = cv2.pyrDown(img)
    r = cv2.pyrDown(r)
    r = cv2.resize(r, [int(y/4),int(x/4)])    # 为啥是4倍呢 因为ESRGAN的配置文件有一个scale的四倍放大 当然这个地方个人可以根据实际的需求处理 此处的参数并不会影响程序运行

    cv2.imwrite("../../datasets/example/bai_hr_sub/" + str(i).split("/")[-1], r)

 4. Execute todo o modelo de treinamento

        Na verdade, para nosso simples treinamento ou comparação de modelos, isso é basicamente suficiente. Se você não deseja modificar seu modelo ou sente que sua escrita não é perfeita o suficiente, você deseja continuar a melhorar sua estrutura. Na verdade, nossos preparativos foram concluídos. A seguir, podemos executar nosso modelo de treinamento diretamente.

Precisamos ir para nossa pasta para executar nosso script de treinamento:


cd BasicSR-master

# 已经在第一步设置了cuda的或者对于自己的gpu可以自由支配的可以使用此运行命令
python3 basicsr/train.py -opt options/train/ESRGAN/train_ESRGAN_x4.yml

# 指定我们的gpu来跑训练模型
CUDA_VISIBLE_DEVICES=2 python3 basicsr/train.py -opt options/train/ESRGAN/train_ESRGAN_x4.yml

# 如果不知道自己的gpu信息可以安装一下gpustat 然后在自己的gpu中直接输入就可以看到了
pip install gpustat

 

Quando a imagem a seguir finalmente aparecer, o treinamento estará concluído.

 Finalmente, nossos resultados de treinamento serão gerados sob nossos experimentos

 

Finalmente, julgamos o efeito com base em nossa relação sinal-ruído de pico. Nossa relação sinal-ruído de pico pode ser vista no registro. No entanto, o seu PSNR pode não ser o mais consistente com os nossos critérios de avaliação. Ainda é preciso considerar outros julgamentos de perda.

Terminar!

Acho que você gosta

Origin blog.csdn.net/qq_38735174/article/details/127922798
Recomendado
Clasificación