Je souhaite utiliser le GAN (Hostile Generation Network) pour colorer automatiquement les images en niveaux de gris.
Il semble être techniquement appelé "pix2pix".
Cette image en échelle de gris est
![0_gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/66e565f7-1c0b-ca9b-2a7c-aabdc47b2977 .png)
J'ai pu colorier automatiquement comme suit !!
![0_fake.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141993/96a8572a- 3471-96ee-e2ce-6328c0d46401.png)
À certains endroits, il y a des parties étranges et certaines images ne fonctionnent pas, mais la coloration est assez naturelle.
À propos, si vous affichez uniquement la ligne du bas de l'image d'origine, Comme ça. Les couleurs des trains et des lits sont différentes, mais j'ai l'impression qu'ils sont peints dans les mêmes tons dans l'ensemble.
L'image approximative de cette étude est la suivante.
Puisqu'il s'agit d'un GAN, nous utilisons deux réseaux, Generator et Discriminator.
(1)
(2)
(3)
(4)
(5)
(6)
De cette manière, le Générateur et le Discriminateur sont formés pour tromper alternativement les deux réseaux.
Cette fois, pytorch 1.1, torchvision 0.30 est utilisé. Pour le moment, importez la bibliothèque à utiliser
import glob
import os
import pickle
import torch
import torch.nn.functional as F
import torchvision
import torch.utils.data as data
import torchvision.transforms as transforms
import numpy as np #1.16.4
import matplotlib.pyplot as plt
from PIL import Image
from torch import nn
from skimage import io
L'environnement est windows10, Anaconda1.9.7, core-i3 8100, RAN 16.0 GB GEFORCE GTX 1060
Le GPU est recommandé car il prend beaucoup de temps d'apprentissage.
3-1.Generator
U-net utilisé pour la segmentation sémantique est utilisé pour Generator. Vous pouvez obtenir une image de sortie avec la même forme que l'image d'entrée sur le réseau Encoder-Decoder. L'image d'entrée est une image grise et l'image de sortie est une image couleur (fausse image). La fonctionnalité de ce U-net est la partie Copie et recadrage. C'est un appareil (apparemment) pour ajouter une sortie proche du calque d'entrée à un calque proche du calque de sortie afin que la forme de l'image d'origine ne soit pas perdue.
Réaliser cette copie et recadrage avec pytorch est assez facile, -Utilisez torch.cat pour combiner les entrées. -Doublez le nombre de canaux d'entrée pour Conv2d et BatchNorm2d. seulement. Quand je l'ai vu pour la première fois, j'ai été très impressionné.
Cependant, il est nécessaire de faire correspondre la forme du tenseur à combiner avec torch.cat.
Si vous utilisez ce U-net tel quel, ce sera un réseau assez énorme. (On dirait qu'il y a environ 18 CNN) Par conséquent, réduisez la taille du réseau et réduisez la taille des images d'entrée / sortie à 3 x 128 x 128.
class Generator(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=5, stride=1, padding=2)
self.bn1 = nn.BatchNorm2d(32)
self.av2 = nn.AvgPool2d(kernel_size=4)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(64)
self.av3 = nn.AvgPool2d(kernel_size=2)
self.conv3 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
self.bn3 = nn.BatchNorm2d(128)
self.av4 = nn.AvgPool2d(kernel_size=2)
self.conv4 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
self.bn4 = nn.BatchNorm2d(256)
self.av5 = nn.AvgPool2d(kernel_size=2)
self.conv5 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
self.bn5 = nn.BatchNorm2d(256)
self.un6 = nn.UpsamplingNearest2d(scale_factor=2)
self.conv6 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
self.bn6 = nn.BatchNorm2d(256)
#La sortie de conv6 et la sortie de conv4 sont envoyées à conv7.,Double canal d'entrée
self.un7 = nn.UpsamplingNearest2d(scale_factor=2)
self.conv7 = nn.Conv2d(256 * 2, 128, kernel_size=3, stride=1, padding=1)
self.bn7 = nn.BatchNorm2d(128)
#Envoyer la sortie de conv7 et la sortie de conv3 à conv8,Double canal d'entrée
self.un8 = nn.UpsamplingNearest2d(scale_factor=2)
self.conv8 = nn.Conv2d(128 * 2, 64, kernel_size=3, stride=1, padding=1)
self.bn8 = nn.BatchNorm2d(64)
#La sortie de conv8 et la sortie de conv2 sont envoyées à conv9.,Double canal d'entrée
self.un9 = nn.UpsamplingNearest2d(scale_factor=4)
self.conv9 = nn.Conv2d(64 * 2, 32, kernel_size=3, stride=1, padding=1)
self.bn9 = nn.BatchNorm2d(32)
self.conv10 = nn.Conv2d(32 * 2, 3, kernel_size=5, stride=1, padding=2)
self.tanh = nn.Tanh()
def forward(self, x):
#x1-x4 est une torche.Parce que j'ai besoin de chat,Partir
x1 = F.relu(self.bn1(self.conv1(x)), inplace=True)
x2 = F.relu(self.bn2(self.conv2(self.av2(x1))), inplace=True)
x3 = F.relu(self.bn3(self.conv3(self.av3(x2))), inplace=True)
x4 = F.relu(self.bn4(self.conv4(self.av4(x3))), inplace=True)
x = F.relu(self.bn5(self.conv5(self.av5(x4))), inplace=True)
x = F.relu(self.bn6(self.conv6(self.un6(x))), inplace=True)
x = torch.cat([x, x4], dim=1)
x = F.relu(self.bn7(self.conv7(self.un7(x))), inplace=True)
x = torch.cat([x, x3], dim=1)
x = F.relu(self.bn8(self.conv8(self.un8(x))), inplace=True)
x = torch.cat([x, x2], dim=1)
x = F.relu(self.bn9(self.conv9(self.un9(x))), inplace=True)
x = torch.cat([x, x1], dim=1)
x = self.tanh(self.conv10(x))
return x
3-2.Discriminator Discriminator est similaire à un réseau normal d'identification d'images. Cependant, la sortie est n x n nombres, pas unidimensionnelle. Affiche True ou False pour chacune de ces zones divisées. Dans le cas de l'image ci-dessous, c'est du 4x4.
Cette technique est appelée patch GAN.
Après cela, la fonction d'activation est le classique Leakly Relu de GAN, InstanceNorm2d est utilisé à la place de BatchNorm2d.
J'ai essayé à la fois InstanceNorm2d et BatchNorm2d, mais je n'ai pas vraiment remarqué beaucoup de différence dans les résultats. InstanceNorm2d était bon pour Pix2Pix, donc j'utilise celui-ci cette fois.
class Discriminator(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 16, kernel_size=5, stride=1, padding=2)
self.in1 = nn.InstanceNorm2d(16)
self.av2 = nn.AvgPool2d(kernel_size=2)
self.conv2_1 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
self.in2_1 = nn.InstanceNorm2d(32)
self.conv2_2 = nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=1)
self.in2_2 = nn.InstanceNorm2d(32)
self.av3 = nn.AvgPool2d(kernel_size=2)
self.conv3_1 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
self.in3_1 = nn.InstanceNorm2d(64)
self.conv3_2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)
self.in3_2 = nn.InstanceNorm2d(64)
self.av4 = nn.AvgPool2d(kernel_size=2)
self.conv4_1 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
self.in4_1 = nn.InstanceNorm2d(128)
self.conv4_2 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1)
self.in4_2 = nn.InstanceNorm2d(128)
self.av5 = nn.AvgPool2d(kernel_size=2)
self.conv5_1 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)
self.in5_1 = nn.InstanceNorm2d(256)
self.conv5_2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
self.in5_2 = nn.InstanceNorm2d(256)
self.av6 = nn.AvgPool2d(kernel_size=2)
self.conv6 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)
self.in6 = nn.InstanceNorm2d(512)
self.conv7 = nn.Conv2d(512, 1, kernel_size=1)
def forward(self, x):
x = F.leaky_relu(self.in1(self.conv1(x)), 0.2, inplace=True)
x = F.leaky_relu(self.in2_1(self.conv2_1(self.av2(x))), 0.2, inplace=True)
x = F.leaky_relu(self.in2_2(self.conv2_2(x)), 0.2, inplace=True)
x = F.leaky_relu(self.in3_1(self.conv3_1(self.av3(x))), 0.2, inplace=True)
x = F.leaky_relu(self.in3_2(self.conv3_2(x)), 0.2, inplace=True)
x = F.leaky_relu(self.in4_1(self.conv4_1(self.av4(x))), 0.2, inplace=True)
x = F.leaky_relu(self.in4_2(self.conv4_2(x)), 0.2, inplace=True)
x = F.leaky_relu(self.in5_1(self.conv5_1(self.av5(x))), 0.2, inplace=True)
x = F.leaky_relu(self.in5_2(self.conv5_2(x)), 0.2, inplace=True)
x = F.leaky_relu(self.in6(self.conv6(self.av6(x))), 0.2, inplace=True)
x = self.conv7(x)
return x
Générer une pseudo image à l'aide de torch.randn Vérifiez la taille de sortie du générateur et du discriminateur.
Ici, deux images d'une taille de 3 x 128 x 128 sont générées et entrées dans le générateur et le discriminateur.
g, d = Generator(), Discriminator()
#Pseudo image par nombre aléatoire
test_imgs = torch.randn([2, 3, 128, 128])
test_imgs = g(test_imgs)
test_res = d(test_imgs)
print("Generator_output", test_imgs.size())
print("Discriminator_output",test_res.size())
La sortie ressemble à ceci:
Generator_output torch.Size([2, 3, 128, 128]) Discriminator_output torch.Size([2, 1, 4, 4])
La taille de sortie du générateur est la même que celle de l'entrée. La taille de sortie de Discriminator est de 4x4.
Cette fois, nous allons acquérir les données selon le flux suivant.
Expansion des données de la partie b.
class DataAugment():
#Augmentation des données de l'image PIL,Retour PIL
def __init__(self, resize):
self.data_transform = transforms.Compose([
transforms.RandomResizedCrop(resize, scale=(0.9, 1.0)),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip()])
def __call__(self, img):
return self.data_transform(img)
Dans la partie qui se convertit en tenseur d, la normalisation des données est également effectuée en même temps.
class ImgTransform():
#Redimensionner l'image PIL,Normaliser et retourner le tenseur
def __init__(self, resize, mean, std):
self.data_transform = transforms.Compose([
transforms.Resize(resize),
transforms.ToTensor(),
transforms.Normalize(mean, std)])
def __call__(self, img):
return self.data_transform(img)
C'est une classe qui hérite de la classe Dataset de Pytorch, et le flux jusqu'à a-d est écrit à la place de getitem. Vous pouvez facilement créer un chargeur de données en créant un flux d'entrée et de sortie pour une image dans la partie getitem.
class MonoColorDataset(data.Dataset):
"""
Hériter de la classe Dataset de Pytorch
"""
def __init__(self, file_list, transform_tensor, augment=None):
self.file_list = file_list
self.augment = augment #PIL to PIL
self.transform_tensor = transform_tensor #PIL to Tensor
def __len__(self):
return len(self.file_list)
def __getitem__(self, index):
#Obtenez le chemin du fichier du numéro d'index
img_path = self.file_list[index]
img = Image.open(img_path)
img = img.convert("RGB")
if self.augment is not None:
img = self.augment(img)
#Copie pour image monochrome
img_gray = img.copy()
#Convertir une image couleur en image monochrome
img_gray = transforms.functional.to_grayscale(img_gray,
num_output_channels=3)
#Convertir PIL en tenseur
img = self.transform_tensor(img)
img_gray = self.transform_tensor(img_gray)
return img, img_gray
En définissant augment = None, les données ne seront pas développées, c'est-à-dire qu'il s'agira d'un ensemble de données pour les données de test. La fonction pour créer le chargeur de données est la suivante.
def load_train_dataloader(file_path, batch_size):
"""
Input
file_chemin Liste des chemins de fichiers pour l'image que vous souhaitez obtenir
batch_size Taille du lot du chargeur de données
return
train_loader, RGB_images and Gray_images
"""
size = 128 #Une taille de côté de l'image
mean = (0.5, 0.5, 0.5) #Valeur moyenne pour chaque canal lorsque l'image est normalisée
std = (0.5, 0.5, 0.5) #Écart type pour chaque canal lorsque l'image est normalisée
#base de données
train_dataset = MonoColorDataset(file_path_train,
transform=ImgTransform(size, mean, std),
augment=DataAugment(size))
#Chargeur de données
train_dataloader = data.DataLoader(train_dataset,
batch_size=batch_size,
shuffle=True)
return train_dataloader
Il est pratique d'utiliser "torchvision.utils.make_grid" pour organiser plusieurs images dans une tuile. Après avoir généré une image en mosaïque avec tensor, convertissez-la en numpy et dessinez-la avec matplotlib.
def mat_grid_imgs(imgs, nrow, save_path = None):
"""
tenseur de pytorch(imgs)Une fonction qui dessine des tuiles
Déterminez le nombre de côtés d'une tuile avec nrow
"""
imgs = torchvision.utils.make_grid(
imgs[0:(nrow**2), :, :, :], nrow=nrow, padding=5)
imgs = imgs.numpy().transpose([1,2,0])
imgs -= np.min(imgs) #La valeur minimale est 0
imgs /= np.max(imgs) #La valeur maximale est 1
plt.imshow(imgs)
plt.xticks([])
plt.yticks([])
plt.show()
if save_path is not None:
io.imsave(save_path, imgs)
Une fonction qui charge une image de test et dessine une image grise et une fausse image en mosaïques.
def evaluate_test(file_path_test, model_G, device="cuda:0", nrow=4):
"""
image de test de charge,Dessinez des images grises et fausses dans des carreaux
"""
model_G = model_G.to(device)
size = 128
mean = (0.5, 0.5, 0.5)
std = (0.5, 0.5, 0.5)
test_dataset = MonoColorDataset(file_path_test,
transform=ImgTransform(size, mean, std),
augment=None)
test_dataloader = data.DataLoader(test_dataset,
batch_size=nrow**2,
shuffle=False)
#Dessinez une image pour chaque chargeur de données
for img, img_gray in test_dataloader:
mat_grid_imgs(img_gray, nrow=nrow)
img = img.to(device)
img_gray = img_gray.to(device)
#img_Du gris à l'aide du générateur,Image RVB de faux
img_fake = model_G(img_gray)
img_fake = img_fake.to("cpu")
img_fake = img_fake.detach()
mat_grid_imgs(img_fake, nrow=nrow)
g = Generator()
file_path_test = glob.glob("test/*")
evaluate_test(file_path_test, g)
Bien que ce soit le résultat avant l'apprentissage, la forme de l'image d'entrée peut être vaguement comprise.
Pour le moment, je saisis juste une grande quantité de données d'image, donc j'entre COCO2014, PASCAL Voc2007, Labeled Faces in the Wild etc. Ces données contiennent une bonne proportion d'images grises. Je veux créer une image en noir et blanc en couleur cette fois, mais l'image qui devrait être un modèle ne peut pas être affichée dans l'image grise (?). Je voudrais donc supprimer l'image grise. Pour les images grises, les couleurs du canal R, du canal G et du canal B doivent être les mêmes, je voudrais donc l'utiliser pour les supprimer. Dans le même temps, les images trop blanches, les images trop sombres et les images qui n'ont pas beaucoup d'ombrage de couleur (l'écart type est petit) sont également extraites.
from skimage import io, color, transform
def color_mono(image, threshold=150):
#Déterminez si l'image d'entrée de 3 canaux est en couleur
#Si vous définissez un seuil élevé, vous pouvez définir Mono même pour les photos aux couleurs légèrement mélangées.
image_size = image.shape[0] * image.shape[1]
#La combinaison des canaux(0, 1),(0, 2),(1, 2)3 façons,Voyez la différence pour chaque canal
diff = np.abs(np.sum(image[:,:, 0] - image[:,:, 1])) / image_size
diff += np.abs(np.sum(image[:,:, 0] - image[:,:, 2])) / image_size
diff += np.abs(np.sum(image[:,:, 1] - image[:,:, 2])) / image_size
if diff > threshold:
return "color"
else:
return "mono"
def bright_check(image, ave_thres = 0.15, std_thres = 0.1):
try:
#Image trop lumineuse,Image trop sombre,Image avec une luminosité similaire Faux
#Convertir en noir et blanc
image = color.rgb2gray(image)
if image.shape[0] < 144:
return False
#Pour les images trop lumineuses
if np.average(image) > (1.-ave_thres):
return False
#Pour les images trop sombres
if np.average(image) < ave_thres:
return False
#Si toute la luminosité est similaire
if np.std(image) < std_thres:
return False
return True
except:
return False
paths = glob.glob("./test2014/*")
for i, path in enumerate(paths):
image = io.imread(path)
save_name = "./trans\\mscoco_" + str(i) +".png "
x = image.shape[0] #Nombre de pixels le long de l'axe x
y = image.shape[1] #Nombre de pixels dans la direction de l'axe y
try:
#Le plus court des axes x et y/2
clip_half = min(x, y)/2
#Découpez un carré dans l'image
image = image[int(x/2 -clip_half): int(x/2 + clip_half),
int(y/2 -clip_half): int(y/2 + clip_half), :]
if color_mono(image) == "color":
if bright_check(image):
image = transform.resize(image, (144, 144, 3),
anti_aliasing = True)
image = np.uint8(image*255)
io.imsave(save_name, image)
except:
pass
J'ai coupé les images en carrés et les ai toutes placées dans un même dossier. L'image mesure 144x144 au lieu de 128x128 afin que les données puissent être développées.
C'est généralement correct, mais pour une raison quelconque, il y avait des omissions et des images de couleur sépia, alors je les ai supprimées manuellement.
J'ai mis environ 110 000 images dans le dossier «trans». Utilisez glob pour créer et charger une liste de chemins d'images.
L'apprentissage prenait environ 20 minutes par époque. Le code est long car nous formons à la fois Générateur et Discriminateur.
Le point à noter est l'étiquette de calcul de la perte, et la taille de la sortie du discriminateur est la taille de la sortie du discriminateur dans la confirmation de 4. J'ai confirmé que ce sera [batch_size, 1, 4, 4], alors faites-le correspondre Génère true_labels et false_labels.
def train(model_G, model_D, epoch, epoch_plus):
device = "cuda:0"
batch_size = 32
model_G = model_G.to(device)
model_D = model_D.to(device)
params_G = torch.optim.Adam(model_G.parameters(),
lr=0.0002, betas=(0.5, 0.999))
params_D = torch.optim.Adam(model_D.parameters(),
lr=0.0002, betas=(0.5, 0.999))
#Étiquette pour le calcul de la perte,Faites attention à la taille du discriminateur
true_labels = torch.ones(batch_size, 1, 4, 4).to(device) #True
false_labels = torch.zeros(batch_size, 1, 4, 4).to(device) #False
#loss_function
bce_loss = nn.BCEWithLogitsLoss()
mae_loss = nn.L1Loss()
#Enregistrer la transition d'erreur
log_loss_G_sum, log_loss_G_bce, log_loss_G_mae = list(), list(), list()
log_loss_D = list()
for i in range(epoch):
#Enregistrer une erreur temporaire
loss_G_sum, loss_G_bce, loss_G_mae = list(), list(), list()
loss_D = list()
train_dataloader = load_train_dataloader(file_path_train, batch_size)
for real_color, input_gray in train_dataloader:
batch_len = len(real_color)
real_color = real_color.to(device)
input_gray = input_gray.to(device)
#Formation générateur
#Générer une fausse image couleur
fake_color = model_G(input_gray)
#Enregistrer temporairement une fausse image
fake_color_tensor = fake_color.detach()
#Calculez la perte afin que la fausse image puisse être trompée comme la vraie chose
LAMBD = 100.0 #Coefficients BCE et MAE
#out quand une fausse image est placée dans le classificateur,D essaie de se rapprocher de 0.
out = model_D(fake_color)
#Perte pour la sortie de D,La cible est vraie parce que je veux rapprocher G de la réalité_labels
loss_G_bce_tmp = bce_loss(out, true_labels[:batch_len])
#Perte pour la sortie G
loss_G_mae_tmp = LAMBD * mae_loss(fake_color, real_color)
loss_G_sum_tmp = loss_G_bce_tmp + loss_G_mae_tmp
loss_G_bce.append(loss_G_bce_tmp.item())
loss_G_mae.append(loss_G_mae_tmp.item())
loss_G_sum.append(loss_G_sum_tmp.item())
#Calculer le gradient,Mise à jour du poids G
params_D.zero_grad()
params_G.zero_grad()
loss_G_sum_tmp.backward()
params_G.step()
#Formation des discriminateurs
real_out = model_D(real_color)
fake_out = model_D(fake_color_tensor)
#Calcul de la fonction de perte
loss_D_real = bce_loss(real_out, true_labels[:batch_len])
loss_D_fake = bce_loss(fake_out, false_labels[:batch_len])
loss_D_tmp = loss_D_real + loss_D_fake
loss_D.append(loss_D_tmp.item())
#Calculer le gradient,Mise à jour du poids D
params_D.zero_grad()
params_G.zero_grad()
loss_D_tmp.backward()
params_D.step()
i = i + epoch_plus
print(i, "loss_G", np.mean(loss_G_sum), "loss_D", np.mean(loss_D))
log_loss_G_sum.append(np.mean(loss_G_sum))
log_loss_G_bce.append(np.mean(loss_G_bce))
log_loss_G_mae.append(np.mean(loss_G_mae))
log_loss_D.append(np.mean(loss_D))
file_path_test = glob.glob("test/*")
evaluate_test(file_path_test, model_G, device)
return model_G, model_D, [log_loss_G_sum, log_loss_G_bce, log_loss_G_mae, log_loss_D]
Effectuer l'apprentissage.
file_path_train = glob.glob("trans/*")
model_G = Generator()
model_D = Discriminator()
model_G, model_D, logs = train(model_G, model_D, 40)
Après 2 époques
cette? On se sent plutôt bien sauf que l'image de l'avion n'est pas du tout peinte ??
Après 11 époque
Après 21 époques
Après 40 fin d'époque (image montrée au début)
De manière inattendue, j'ai senti que l'image après 2 époques était bonne ...
Je publierai également d'autres images. 11 Après la fin de l'époque. J'ai sélectionné beaucoup d'images qui semblent avoir échoué. L'image terrible est vraiment terrible, avec presque aucune couleur Comme l'image du baseball, je le peins sans ignorer la frontière.
Je sens que je suis bon pour les verts comme l'herbe et les arbres, et les bleus comme le ciel. Cela semble dépendre du biais de l'ensemble de données d'origine et de la facilité de la peinture (facilité de reconnaissance).
L'image grise a été colorisée à l'aide de pix2pix.
Cette fois, j'ai décidé d'ajouter une image et de faire une image couleur dès que je pouvais faire quoi que ce soit. Comme le réseau est peu profond, l'expressivité est faible, donc Je pense que la réduction des types d'images fonctionne mieux.
Pour être honnête, je pense que c'est plus facile à comprendre que ce que j'ai écrit.
U-Net: Convolutional Networks for Biomedical Image Segmentation https://lmb.informatik.uni-freiburg.de/people/ronneber/u-net/
J'ai implémenté pix2pix à partir de 1 et j'ai essayé de coloriser une image en noir et blanc (PyTorch) https://blog.shikoan.com/pytorch_pix2pix_colorization/
pix2 je veux comprendre pix https://qiita.com/mine820/items/36ffc3c0aea0b98027fd
CoCo https://cocodataset.org/#home
Labeled Faces in the Wild http://vis-www.cs.umass.edu/lfw/
The PASCAL Visual Object Classes Homepage http://host.robots.ox.ac.uk/pascal/VOC/
Recommended Posts