Fresque-SETI/Apprentissage_MSELoss_avec_GPU.py

425 lines
15 KiB
Python
Raw Normal View History

2021-01-08 19:41:57 +01:00
#!/usr/bin/env python
# coding: utf-8
# In[1]:
#Tous les codes sont basés sur l'environnement suivant
#python 3.7
#opencv 3.1.0
#pytorch 1.4.0
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import cv2
import matplotlib.pyplot as plt
import numpy as np
import random
import math
import pickle
import random
from PIL import Image
import sys
# In[3]:
#Les fonctions dans ce bloc ne sont pas utilisées par le réseau, mais certaines fonctions d'outils
def tensor_imshow(im_tensor,cannel):
b,c,h,w=im_tensor.shape
if c==1:
plt.imshow(im_tensor.squeeze().detach().numpy())
else:
plt.imshow(im_tensor.squeeze().detach().numpy()[cannel,:])
# Obtenez des données d'entraînement
# frag,vt=get_training_fragment(frag_size,image)
# frag est un patch carrée de taille (frag_size*frag_size) a partir du image(Son emplacement est aléatoire)
# vt est la vérité terrain de la forme Dirac.
def get_training_fragment(frag_size,im):
h,w,c=im.shape
n=random.randint(0,int(h/frag_size)-1)
m=random.randint(0,int(w/frag_size)-1)
shape=frag_size/4
vt_h=math.ceil((h+1)/shape)
vt_w=math.ceil((w+1)/shape)
vt=np.zeros([vt_h,vt_w])
vt_h_po=round((vt_h-1)*(n*frag_size/(h-1)+(n+1)*frag_size/(h-1))/2)
vt_w_po=round((vt_w-1)*(m*frag_size/(w-1)+(m+1)*frag_size/(w-1))/2)
vt[vt_h_po,vt_w_po]=1
vt = np.float32(vt)
vt=torch.from_numpy(vt.reshape(1,1,vt_h,vt_w))
return im[n*frag_size:(n+1)*frag_size,m*frag_size:(m+1)*frag_size,:],vt
# Cette fonction convertit l'image en variable de type Tensor.
# Toutes les données de calcul du réseau sont de type Tensor
# Img.shape=[Height,Width,Channel]
# Tensor.shape=[Batch,Channel,Height,Width]
def img2tensor(im):
im=np.array(im,dtype="float32")
tensor_cv = torch.from_numpy(np.transpose(im, (2, 0, 1)))
im_tensor=tensor_cv.unsqueeze(0)
return im_tensor
# Trouvez les coordonnées de la valeur maximale dans une carte de corrélation
# x,y=show_coordonnee(carte de corrélation)
def show_coordonnee(position_pred):
map_corre=position_pred.squeeze().detach().numpy()
h,w=map_corre.shape
max_value=map_corre.max()
coordonnee=np.where(map_corre==max_value)
return coordonnee[0].mean()/h,coordonnee[1].mean()/w
# Filtrer les patchs en fonction du nombre de pixels noirs dans le patch
# Si seuls les pixels non noirs sont plus grands qu'une certaine proportion(seuillage), revenez à True, sinon False
def test_fragment32_32(frag,seuillage):
a=frag[:,:,0]+frag[:,:,1]+frag[:,:,2]
mask = (a == 0)
arr_new = a[mask]
if arr_new.size/a.size<=(1-seuillage):
return True
else:
return False
# Ces deux fonctions permettent de sauvegarder le réseau dans un fichier
# ou de load le réseau stocké à partir d'un fichier
def save_net(file_path,net):
pkl_file = open(file_path, 'wb')
pickle.dump(net,pkl_file)
pkl_file.close()
def load_net(file_path):
pkl_file = open(file_path, 'rb')
net= pickle.load(pkl_file)
pkl_file.close()
return net
# In[4]:
# Les fonctions de ce bloc sont utilisées pour construire le réseau
# Créer un poids de type DeepMatch comme valeur initiale de Conv1 (non obligatoire)
def ini():
kernel=torch.zeros([8,3,3,3])
array_0=np.array([[1,2,1],[0,0,0],[-1,-2,-1]],dtype='float32')
array_1=np.array([[2,1,0],[1,0,-1],[0,-1,-2]],dtype='float32')
array_2=np.array([[1,0,-1],[2,0,-2],[1,0,-1]],dtype='float32')
array_3=np.array([[0,-1,-2],[1,0,-1],[2,1,0]],dtype='float32')
array_4=np.array([[-1,-2,-1],[0,0,0],[1,2,1]],dtype='float32')
array_5=np.array([[-2,-1,0],[-1,0,1],[0,1,2]],dtype='float32')
array_6=np.array([[-1,0,1],[-2,0,2],[-1,0,1]],dtype='float32')
array_7=np.array([[0,1,2],[-1,0,1],[-2,-1,0]],dtype='float32')
for i in range(3):
kernel[0,i,:]=torch.from_numpy(array_0)
kernel[1,i,:]=torch.from_numpy(array_1)
kernel[2,i,:]=torch.from_numpy(array_2)
kernel[3,i,:]=torch.from_numpy(array_3)
kernel[4,i,:]=torch.from_numpy(array_4)
kernel[5,i,:]=torch.from_numpy(array_5)
kernel[6,i,:]=torch.from_numpy(array_6)
kernel[7,i,:]=torch.from_numpy(array_7)
return torch.nn.Parameter(kernel,requires_grad=True)
# Calculer le poids initial de la couche convolutive add
# n, m signifie qu'il y a n * m sous-patches dans le patch d'entrée
# Par exemple, le patch d'entrée est 16 * 16, pour les patchs 4 * 4 de la première couche, n = 4, m = 4
# pour les patchs 8 * 8 de la deuxième couche, n = 2, m = 2
def kernel_add_ini(n,m):
input_canal=int(n*m)
output_canal=int(n/2)*int(m/2)
for i in range(int(n/2)):
for j in range(int(m/2)):
kernel_add=np.zeros([1,input_canal],dtype='float32')
kernel_add[0,i*2*m+j*2]=1
kernel_add[0,i*2*m+j*2+1]=1
kernel_add[0,(i*2+1)*m+j*2]=1
kernel_add[0,(i*2+1)*m+j*2+1]=1
if i==0 and j==0:
add=torch.from_numpy(kernel_add.reshape(1,input_canal,1,1))
else:
add_=torch.from_numpy(kernel_add.reshape(1,input_canal,1,1))
add=torch.cat((add,add_),0)
return torch.nn.Parameter(add,requires_grad=False)
# Calculer le poids initial de la couche convolutive shift
# shift+add Peut réaliser l'étape de l'agrégation
# Voir ci-dessus pour les paramètres n et m.
# Pour des étapes plus détaillées, veuillez consulter mon rapport de stage
def kernel_shift_ini(n,m):
input_canal=int(n*m)
output_canal=int(n*m)
kernel_shift=torch.zeros([output_canal,input_canal,3,3])
array_0=np.array([[1,0,0],[0,0,0],[0,0,0]],dtype='float32')
array_1=np.array([[0,0,1],[0,0,0],[0,0,0]],dtype='float32')
array_2=np.array([[0,0,0],[0,0,0],[1,0,0]],dtype='float32')
array_3=np.array([[0,0,0],[0,0,0],[0,0,1]],dtype='float32')
kernel_shift_0=torch.from_numpy(array_0)
kernel_shift_1=torch.from_numpy(array_1)
kernel_shift_2=torch.from_numpy(array_2)
kernel_shift_3=torch.from_numpy(array_3)
for i in range(n):
for j in range(m):
if i==0 and j==0:
kernel_shift[0,0,:]=kernel_shift_0
else:
if i%2==0 and j%2==0:
kernel_shift[i*m+j,i*m+j,:]=kernel_shift_0
if i%2==0 and j%2==1:
kernel_shift[i*m+j,i*m+j,:]=kernel_shift_1
if i%2==1 and j%2==0:
kernel_shift[i*m+j,i*m+j,:]=kernel_shift_2
if i%2==1 and j%2==1:
kernel_shift[i*m+j,i*m+j,:]=kernel_shift_3
return torch.nn.Parameter(kernel_shift,requires_grad=False)
# Trouvez le petit patch(4 * 4) dans la n ème ligne et la m ème colonne du patch d'entrée
# Ceci est utilisé pour calculer la convolution et obtenir la carte de corrélation
def get_patch(fragment,psize,n,m):
return fragment[:,:,n*psize:(n+1)*psize,m*psize:(m+1)*psize]
###################################################################################################################
class Net(nn.Module):
def __init__(self,frag_size,psize):
super(Net, self).__init__()
h_fr=frag_size
w_fr=frag_size
n=int(h_fr/psize) # n*m patches dans le patch d'entrée
m=int(w_fr/psize)
self.conv1 = nn.Conv2d(3,8,kernel_size=3,stride=1,padding=1)
# Si vous souhaitez initialiser Conv1 avec les poids de DeepMatch, exécutez la ligne suivante
# self.conv1.weight=ini()
self.Relu = nn.ReLU(inplace=True)
self.maxpooling=nn.MaxPool2d(3,stride=2, padding=1)
self.shift1=nn.Conv2d(n*m,n*m,kernel_size=3,stride=1,padding=1)
self.shift1.weight=kernel_shift_ini(n,m)
self.add1 = nn.Conv2d(n*m,int(n/2)*int(m/2),kernel_size=1,stride=1,padding=0)
self.add1.weight=kernel_add_ini(n,m)
n=int(n/2)
m=int(m/2)
if n>=2 and m>=2:# Si n=m=1Notre réseau n'a plus besoin de plus de couches pour agréger les cartes de corrélation
self.shift2=nn.Conv2d(n*m,n*m,kernel_size=3,stride=1,padding=1)
self.shift2.weight=kernel_shift_ini(n,m)
self.add2 = nn.Conv2d(n*m,int(n/2)*int(m/2),kernel_size=1,stride=1,padding=0)
self.add2.weight=kernel_add_ini(n,m)
n=int(n/2)
m=int(m/2)
if n>=2 and m>=2:
self.shift3=nn.Conv2d(n*m,n*m,kernel_size=3,stride=1,padding=1)
self.shift3.weight=kernel_shift_ini(n,m)
self.add3 = nn.Conv2d(n*m,int(n/2)*int(m/2),kernel_size=1,stride=1,padding=0)
self.add3.weight=kernel_add_ini(n,m)
def get_descripteur(self,img,using_cuda):
# Utilisez Conv1 pour calculer le descripteur,
descripteur_img=self.Relu(self.conv1(img))
b,c,h,w=descripteur_img.shape
couche_constante=0.5*torch.ones([1,1,h,w])
if using_cuda:
couche_constante=couche_constante.cuda()
# Ajouter une couche constante pour éviter la division par 0 lors de la normalisation
descripteur_img=torch.cat((descripteur_img,couche_constante),1)
# la normalisation
descripteur_img_norm=descripteur_img/torch.norm(descripteur_img,dim=1)
return descripteur_img_norm
def forward(self,img,frag,using_cuda):
psize=4
# Utilisez Conv1 pour calculer le descripteur,
descripteur_input1=self.get_descripteur(img,using_cuda)
descripteur_input2=self.get_descripteur(frag,using_cuda)
b,c,h,w=frag.shape
n=int(h/psize)
m=int(w/psize)
#######################################
# Calculer la carte de corrélation par convolution pour les n*m patchs plus petit.
for i in range(n):
for j in range(m):
if i==0 and j==0:
map_corre=F.conv2d(descripteur_input1,get_patch(descripteur_input2,psize,i,j),padding=2)
else:
a=F.conv2d(descripteur_input1,get_patch(descripteur_input2,psize,i,j),padding=2)
map_corre=torch.cat((map_corre,a),1)
########################################
# Étape de polymérisation
map_corre=self.maxpooling(map_corre)
map_corre=self.shift1(map_corre)
map_corre=self.add1(map_corre)
#########################################
# Répétez l'étape d'agrégation jusqu'à obtenir le graphique de corrélation du patch d'entrée
n=int(n/2)
m=int(m/2)
if n>=2 and m>=2:
map_corre=self.maxpooling(map_corre)
map_corre=self.shift2(map_corre)
map_corre=self.add2(map_corre)
n=int(n/2)
m=int(m/2)
if n>=2 and m>=2:
map_corre=self.maxpooling(map_corre)
map_corre=self.shift3(map_corre)
map_corre=self.add3(map_corre)
b,c,h,w=map_corre.shape
# Normalisation de la division par maximum
map_corre=map_corre/(map_corre.max())
# Normalisation SoftMax
#map_corre=(F.softmax(map_corre.reshape(1,1,h*w,1),dim=2)).reshape(b,c,h,w)
return map_corre
# In[5]:
def run_net(net,img,frag,frag_size,using_cuda):
h,w,c=frag.shape
n=int(h/frag_size)
m=int(w/frag_size)
frag_list=[]
#####################################
# Obtenez des patchs carrés des fragments et mettez-les dans la frag_list
for i in range(n):
for j in range(m):
frag_32=frag[i*frag_size:(i+1)*frag_size,j*frag_size:(j+1)*frag_size]
if test_fragment32_32(frag_32,0.6):
frag_list.append(frag_32)
img_tensor=img2tensor(img)
######################################
if using_cuda:
img_tensor=img_tensor.cuda()
coordonnee_list=[]
#######################################
# Utilisez le réseau pour calculer les positions de tous les patchs dans frag_list[]
# Mettez le résultat du calcul dans coordonnee_list[]
for i in range(len(frag_list)):
frag_tensor=img2tensor(frag_list[i])
if using_cuda:
frag_tensor=frag_tensor.cuda()
res=net.forward(img_tensor,frag_tensor,using_cuda)
if using_cuda:
res=res.cpu()
po_h,po_w=show_coordonnee(res)
coordonnee_list.append([po_h,po_w])
h_img,w_img,c=img.shape
position=[]
for i in range(len(coordonnee_list)):
x=int(round(h_img*coordonnee_list[i][0]))
y=int(round(w_img*coordonnee_list[i][1]))
position.append([x,y])
return position
# In[10]:
if __name__=='__main__':
# La taille du patch d'entrée est de 16*16
frag_size=16
# La taille du plus petit patch dans réseau est de 4 *4 fixée
psize=4
using_cuda=True
net=Net(frag_size,psize)
# Pour chaque fresque, le nombre d'itérations est de 1000
itera=1000
if using_cuda:
net=net.cuda()
# Choisissez l'optimiseur et la fonction de coût
optimizer = torch.optim.Adam(net.parameters())
loss_func = torch.nn.MSELoss()
# Dans le processus d'apprentissage du réseau,le changement d'erreur est placé dans loss_value=[]
# et le changement de Conv1 poids est placé dans para_value[]
loss_value=[]
para_value=[]
####################################################training_net
#Les données d'entraînement sont 6 fresques
for n in range(6):
im_path="./fresque"+str(n)+".ppm"
img_training=cv2.imread(im_path)
h,w,c=img_training.shape
# Si la peinture murale est trop grande, sous-échantillonnez-la et rétrécissez-la
while h*w>(1240*900):
img_training=cv2.resize(img_training,(int(h/2),int(w/2)),interpolation=cv2.INTER_CUBIC)
h,w,c=img_training.shape
im_tensor=img2tensor(img_training)
if using_cuda:
im_tensor=im_tensor.cuda()
for i in range(itera):
# Tous les 100 cycles, enregistrez le changement de poids
if i%100==0:
para=net.conv1.weight
para=para.detach().cpu()
para_value.append(para)
frag,vt=get_training_fragment(frag_size,img_training)
frag_tensor=img2tensor(frag)
if using_cuda:
vt=vt.cuda()
frag_tensor=frag_tensor.cuda()
# Utilisez des patchs et des fresques de données d'entraînement pour faire fonctionner le réseau
frag_pred=net.forward(im_tensor,frag_tensor,using_cuda)
b,c,h,w=vt.shape
# Utilisez la fonction de coût pour calculer l'erreur
err_=loss_func(vt,frag_pred)
# Utilisez l'optimiseur pour ajuster le poids de Conv1
optimizer.zero_grad()
err_.backward(retain_graph=True)
optimizer.step()
loss_value.append(err_.tolist())
del frag_tensor,frag_pred,err_,vt
torch.cuda.empty_cache()
# In[7]:
len(loss_value)
# In[11]:
plt.plot(loss_value)
# In[12]:
file_path="./net_trainned6000"
save_net(file_path,net)