Skip to content
Snippets Groups Projects
Commit 0abfbe48 authored by Hatef OTROSHI's avatar Hatef OTROSHI
Browse files

[ADD] scripts

parent e0fd3ecd
No related branches found
No related tags found
No related merge requests found
Showing
with 4464 additions and 0 deletions
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
'''
Note: If you use this implementation, please cite the following paper:
- Hatef Otroshi Shahreza and Sébastien Marcel. "Breaking Template
Protection: Reconstruction of Face Images from Protected Facial
Templates", IEEE 18th International Conference on Automatic Face
and Gesture Recognition (FG), 2024.
'''
import argparse
parser = argparse.ArgumentParser(description='Generate training dataset for face reconstruction')
parser.add_argument('--FR_system', metavar='<FR_system>', type= str, default='ArcFace',
help='ArcFace/ElasticFace (target FR system)')
args = parser.parse_args()
from numpy.lib.type_check import imag
import torch
from torch.utils.data import Dataset
import sys
import os
sys.path.append(os.path.dirname(os.getcwd()))
from TrainNetwork.src.loss.FaceIDLoss import get_FaceRecognition_transformer
import cv2
import os
import glob
import random
import numpy as np
from DataAug import get_data_aug, Crop_fixed
seed=2021
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
class Generate_Dataset():
def __init__(self, dataset_dir = './Flickr-Faces-HQ/images1024x1024',
device='cpu',
FR_system='ArcFace'
):
self.device=device
self.dir_all_images = []
all_folders = glob.glob(dataset_dir+'/*')
all_folders.sort()
for folder in all_folders:
all_imgs = glob.glob(folder+'/*.png')
all_imgs.sort()
for img in all_imgs:
self.dir_all_images.append(img)
self.Face_Recognition_Network = get_FaceRecognition_transformer(FR_system=FR_system, device=self.device)
# self.Face_Recognition_Network = IResnet100(memory_demanding=False, device=self.device)
def generate_dataset(self, save_dir, n_augment=10):
os.makedirs(f'{save_dir}/images',exist_ok=True)
os.makedirs(f'{save_dir}/embeddings',exist_ok=True)
for n in range(n_augment+1):
print(n)
os.makedirs(f'{save_dir}/embeddings/{n}',exist_ok=True)
for idx in range(len(self.dir_all_images)):
if n ==0:
embedding, image = self.__getitem__(idx, augment=False)
os.makedirs(f'{save_dir}/embeddings/{n}/{self.dir_all_images[idx][-12:-10]}',exist_ok=True)
os.makedirs(f'{save_dir}/images/{self.dir_all_images[idx][-12:-10]}',exist_ok=True)
np.save(f'{save_dir}/images/{self.dir_all_images[idx][-12:-4]}', image)
np.save(f'{save_dir}/embeddings/{n}/{self.dir_all_images[idx][-12:-4]}', embedding)
else:
embedding, image = self.__getitem__(idx, augment=True, n_aug=n)
os.makedirs(f'{save_dir}/embeddings/{n}/{self.dir_all_images[idx][-12:-10]}',exist_ok=True)
np.save(f'{save_dir}/embeddings/{n}/{self.dir_all_images[idx][-12:-4]}', embedding)
def __getitem__(self, idx, augment=True, n_aug=0):
image = cv2.imread(self.dir_all_images[idx]) # (112, 112, 3)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image = image/255.
if augment:
# augment
image = get_data_aug(image, seed=int(n_aug*1e6 +idx) ) # (112, 112, 3)
else:
# don't augment
image = Crop_fixed(image)
image = image.transpose(2,0,1) # (3, 112, 112)
image = np.expand_dims(image, axis=0) # (1, 3, 112, 112)
img = torch.Tensor( (image*255.).astype('uint8') ).type(torch.FloatTensor)
embedding = self.Face_Recognition_Network.transform(img.to(self.device) )
# embedding = self.Face_Recognition_Network.transform( (image*255.).astype('uint8') )
image = image[0] # range (0,1) and shape (3, 112, 112)
# image = self.transform_image(image)
embedding = self.transform_embedding(embedding)
return embedding, image
def transform_image(self,image):
return image
def transform_embedding(self,embedding):
embedding = embedding.cpu().numpy()
embedding = np.reshape(embedding,[embedding.shape[-1],1,1])
return embedding
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print("************ NOTE: The torch device is:", device)
DB_gen = Generate_Dataset(FR_system=args.FR_system, device=device)
DB_gen.generate_dataset(save_dir=f'./datasets/{args.FR_system}')
\ No newline at end of file
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
from .btp import BioHashing, MLPHashing, IoM_GRP, RDM
from .quantization import Equal_probable
import torch
from torch.utils.data import Dataset
import bob.io.base
import glob
import random
import numpy as np
seed=2021
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
class MyDataset(Dataset):
def __init__(self, dataset_dir ,
train=True,
user_seed=0,
btp = None,
mixID_TrainTest=True,
train_test_split = 0.9,
random_split_seed=2021
):
self.dataset_dir = dataset_dir
self.train = train
self.btp = btp
self.user_seed = user_seed
self.dir_all_images = []
for folder in range(70):
for npyfile in range(1000):
self.dir_all_images.append(f"{folder:02d}/{folder:02d}{npyfile:03d}.npy")
if mixID_TrainTest:
random.seed(user_seed)
random.shuffle(self.dir_all_images)
if self.train:
self.dir_all_images = self.dir_all_images[:int(train_test_split*len(self.dir_all_images))]
else:
self.dir_all_images = self.dir_all_images[int(train_test_split*len(self.dir_all_images)):]
if btp==None:
print(f'btp is not defined!')
if btp=='BioHashing':
self.protect= BioHashing.protect
elif btp=='MLPHashing':
self.protect= MLPHashing.protect
elif btp=='IoM_GRP':
self.protect= IoM_GRP.protect
elif btp=='RDM':
self.protect= RDM.protect
elif btp=='HE_BFV':
n_quantization_levels = 8
self.quantization_levels = np.arange(n_quantization_levels-1) + 1
def protect(x, user_seed=0):
return Equal_probable.quantize_vector(x, self.quantization_levels)
self.protect= protect
else:
print(f'BTP is not defined and therefore no protection is applied!')
def protect(x, user_seed):
return x
self.protect = protect
self.len_protected = len(self.protect(np.zeros(512), user_seed=self.user_seed))
def load_quantization_levels(self, path):
self.quantization_levels = np.load(path)
def find_quantization_levels_and_save(self, path, n_quantization_levels=8):
all_data=[]
for idx in range(len(self.dir_all_images)//2):
embedding = f"{self.dataset_dir}/embeddings/{self.dir_all_images[idx]}"
embedding = np.load(embedding) # shape (512,1,1)
all_data.append(embedding[:,0,0])
all_data= np.array(all_data)
self.quantization_levels = Equal_probable.find_q_levels(all_data, n_quantization_levels=n_quantization_levels)
np.save(path,self.quantization_levels)
def __len__(self):
return len(self.dir_all_images)
def __getitem__(self, idx):
image = f"{self.dataset_dir}/images/{self.dir_all_images[idx]}"
embedding = f"{self.dataset_dir}/embeddings/{self.dir_all_images[idx]}"
image = np.load(image) # range (0,1), shape (3, 112, 112)
embedding = np.load(embedding) # shape (512,1,1)
embedding = self.protect(embedding[:,0,0], user_seed= self.user_seed).reshape(-1,1,1)
image = self.transform_image(image)
embedding = self.transform_embedding(embedding,)
return embedding, image
def transform_image(self,image):
# ======= RESIZE ========
# image = image/255.
image = torch.Tensor(image)#.to(self.device)
return image
def transform_embedding(self, embedding):
# embedding = np.reshape(embedding,[embedding.shape[-1],1,1])
embedding = torch.Tensor(embedding)#.to(self.device)
return embedding
\ No newline at end of file
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
import torch.nn as nn
class Generator(nn.Module):
def __init__(self, length_of_embedding):
super(Generator, self).__init__()
# nz is the length of the z input vector,
# ngf relates to the size of the feature maps that are propagated through the generator,
# and nc is the number of channels in the output image (set to 3 for RGB images).
nz = length_of_embedding #128
nc = 3
#
# input is Z, going into a convolution
self.dconv1 = nn.ConvTranspose2d( length_of_embedding, 512, 4, 1, 0, bias=False)
self.BN_dconv1 = nn.BatchNorm2d(512)
self.conv1_1 = nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv1_1 = nn.BatchNorm2d(512)
self.conv1_2 = nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv1_2 = nn.BatchNorm2d(512)
self.conv1_3 = nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv1_3 = nn.BatchNorm2d(512)
# state size. (512) x 4 x 4
self.dconv2 = nn.ConvTranspose2d(512, 256, 4, 2, 1, bias=False)
self.BN_dconv2 = nn.BatchNorm2d(256)
self.conv2_1 = nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv2_1 = nn.BatchNorm2d(256)
self.conv2_2 = nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv2_2 = nn.BatchNorm2d(256)
self.conv2_3 = nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv2_3 = nn.BatchNorm2d(256)
# state size. (256) x 8 x 8
self.dconv3 = nn.ConvTranspose2d( 256, 128, 4, 2, 1, bias=False)
self.BN_dconv3 = nn.BatchNorm2d(128)
self.conv3_1 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv3_1 = nn.BatchNorm2d(128)
self.conv3_2 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv3_2 = nn.BatchNorm2d(128)
self.conv3_3 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv3_3 = nn.BatchNorm2d(128)
# state size. (128) x 16 x 16
self.dconv4 = nn.ConvTranspose2d(128, 64, 4, 2, 2, bias=False)
self.BN_dconv4 = nn.BatchNorm2d(64)
self.conv4_1 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv4_1 = nn.BatchNorm2d(64)
self.conv4_2 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv4_2 = nn.BatchNorm2d(64)
self.conv4_3 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv4_3 = nn.BatchNorm2d(64)
# state size. (64) x 30 x 30
self.dconv5 = nn.ConvTranspose2d(64, 32, 4, 2, 2, bias=False)
self.BN_dconv5 = nn.BatchNorm2d(32)
self.conv5_1 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv5_1 = nn.BatchNorm2d(32)
self.conv5_2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv5_2 = nn.BatchNorm2d(32)
self.conv5_3 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv5_3 = nn.BatchNorm2d(32)
# state size. (32) x 58 x 58
self.dconv6 = nn.ConvTranspose2d( 32, 16, 4, 2, 3, bias=False) #112
# self.dconv6 = nn.ConvTranspose2d( 32, 16, 3, 3, 7, bias=False) #160
self.BN_dconv6 = nn.BatchNorm2d(16)
self.conv6_1 = nn.Conv2d(in_channels=16, out_channels=16, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv6_1 = nn.BatchNorm2d(16)
self.conv6_2 = nn.Conv2d(in_channels=16, out_channels=16, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv6_2 = nn.BatchNorm2d(16)
self.conv6_3 = nn.Conv2d(in_channels=16, out_channels=16, kernel_size=3, stride=1, bias=True, padding=1)
self.BN_conv6_3 = nn.BatchNorm2d(16)
# state size. (nc) x 116 x 116
# state size. (nc) x 160 x 160
self.conv7_1 = nn.Conv2d(in_channels=16, out_channels=nc, kernel_size=1, stride=1, bias=True, padding=0)
self.conv7_3 = nn.Conv2d(in_channels=16, out_channels=nc, kernel_size=3, stride=1, bias=True, padding=1)
self.conv7_5 = nn.Conv2d(in_channels=16, out_channels=nc, kernel_size=5, stride=1, bias=True, padding=2)
self.conv7_7 = nn.Conv2d(in_channels=16, out_channels=nc, kernel_size=7, stride=1, bias=True, padding=3)
def forward(self, x):
x_ = self.dconv1(x)
x_ = self.BN_dconv1(x_)
xd = nn.ReLU(True)(x_)
x_ = self.conv1_1(xd)
x_ = self.BN_conv1_1(x_)
xc = nn.ReLU(True)(x_) + xd
x_ = self.conv1_2(xc)
x_ = self.BN_conv1_2(x_)
xc = nn.ReLU(True)(x_) + xc
x_ = self.conv1_3(xc)
x_ = self.BN_conv1_3(x_)
xc = nn.ReLU(True)(x_) + xc
xd = xd +xc
# deconv 2
x_ = self.dconv2(xd)
x_ = self.BN_dconv2(x_)
xd = nn.ReLU(True)(x_)
x_ = self.conv2_1(xd)
x_ = self.BN_conv2_1(x_)
xc = nn.ReLU(True)(x_) + xd
x_ = self.conv2_2(xc)
x_ = self.BN_conv2_2(x_)
xc = nn.ReLU(True)(x_) + xc
x_ = self.conv2_3(xc)
x_ = self.BN_conv2_3(x_)
xc = nn.ReLU(True)(x_) + xc
xd = xd +xc
# deconv 3
x_ = self.dconv3(xd)
x_ = self.BN_dconv3(x_)
xd = nn.ReLU(True)(x_)
x_ = self.conv3_1(xd)
x_ = self.BN_conv3_1(x_)
xc = nn.ReLU(True)(x_) + xd
x_ = self.conv3_2(xc)
x_ = self.BN_conv3_2(x_)
xc = nn.ReLU(True)(x_) + xc
x_ = self.conv3_3(xc)
x_ = self.BN_conv3_3(x_)
xc = nn.ReLU(True)(x_) + xc
xd = xd +xc
# deconv 4
x_ = self.dconv4(xd)
x_ = self.BN_dconv4(x_)
xd = nn.ReLU(True)(x_)
x_ = self.conv4_1(xd)
x_ = self.BN_conv4_1(x_)
xc = nn.ReLU(True)(x_) + xd
x_ = self.conv4_2(xc)
x_ = self.BN_conv4_2(x_)
xc = nn.ReLU(True)(x_) + xc
x_ = self.conv4_3(xc)
x_ = self.BN_conv4_3(x_)
xc = nn.ReLU(True)(x_) + xc
xd = xd +xc
# deconv 5
x_ = self.dconv5(xd)
x_ = self.BN_dconv5(x_)
xd = nn.ReLU(True)(x_)
x_ = self.conv5_1(xd)
x_ = self.BN_conv5_1(x_)
xc = nn.ReLU(True)(x_) + xd
x_ = self.conv5_2(xc)
x_ = self.BN_conv5_2(x_)
xc = nn.ReLU(True)(x_) + xc
x_ = self.conv5_3(xc)
x_ = self.BN_conv5_3(x_)
xc = nn.ReLU(True)(x_) + xc
xd = xd +xc
# deconv 6
x_ = self.dconv6(xd)
x_ = self.BN_dconv6(x_)
xd = nn.ReLU(True)(x_)
x_ = self.conv6_1(xd)
x_ = self.BN_conv6_1(x_)
xc = nn.ReLU(True)(x_) + xd
x_ = self.conv6_2(xc)
x_ = self.BN_conv6_2(x_)
xc = nn.ReLU(True)(x_) + xc
x_ = self.conv6_3(xc)
x_ = self.BN_conv6_3(x_)
xc = nn.ReLU(True)(x_) + xc
xd = xd +xc
# deconv 7
x_1 = self.conv7_1(xd)
x_3 = self.conv7_3(xd)
x_5 = self.conv7_5(xd)
x_7 = self.conv7_7(xd)
x_ = x_1 + x_3 + x_5 + x_7
xd = nn.Sigmoid()(x_)
return xd
\ No newline at end of file
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
"""
BioHash paper:
- Andrew Teoh Beng Jin, David Ngo Chek Ling, and Alwyn Goh. "Biohashing: two factor authentication featuring fingerprint data and tokenised random number." Pattern recognition 37, no. 11 (2004): 2245-2255.
Note: If you use this implementation, please cite [at least one of] the following papers:
- Hatef Otroshi Shahreza, Vedrana Krivokuća Hahn, and Sébastien Marcel. "On the Recognition Performance of BioHashing on state-of-the-art Face Recognition models." In 2021 IEEE International Workshop on Information Forensics and Security (WIFS), pp. 1-6. IEEE, 2021.
- Hatef Otroshi Shahreza, and Sébastien Marcel. "Towards Protecting and Enhancing Vascular Biometric Recognition methods via Biohashing and Deep Neural Networks." IEEE Transactions on Biometrics, Behavior, and Identity Science, 2021.
"""
import numpy
import scipy.spatial
import hashlib
def protect(feat_vec, user_seed, bh_len=512):
"""Creates a BioHash by projecting the input biometric feature vector onto a set of randomly generated basis vectors and then binarising the resulting vector
**Parameters:**
feat_vec (array): The extracted fingervein feture vector
bh_len (int): The desired length (i.e., number of bits) of the resulting BioHash
user_seed (int): The seed used to generate the user's specific random projection matrix
**Returns:**
biohash (array): The resulting BioHash, which is a protected, binary representation of the input feature vector
"""
numpy.random.seed(
user_seed
) # re-seed the random number generator according to the user's specific seed
rand_mat = numpy.random.rand(
len(feat_vec), bh_len
) # generate matrix of random values from uniform distribution over [0, 1]
orth_mat, _ = numpy.linalg.qr(
rand_mat, mode="reduced"
) # orthonormalise columns of random matrix, mode='reduced' returns orth_mat with size len(feat_vec) x bh_len
biohash = numpy.dot(feat_vec, orth_mat)
thresh = numpy.mean(
biohash
) # threshold by which to binarise vector of dot products to generate final BioHash
biohash = numpy.where(biohash > thresh, 1, 0)
return biohash
def score(model, probe):
"""score(model, probe) -> score
This function will compute the Hamming distance between the given model and probe BioHashes.
**Parameters:**
model : object
The model BioHash to compare the probe BioHash with.
probe : object
The probe BioHash to compare the model BioHash with.
**Returns:**
score : float
The Hamming distance between ``model`` and ``probe``.
Higher values define higher similarities.
"""
return scipy.spatial.distance.hamming(model, probe) * -1
\ No newline at end of file
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
"""
MLP-Hash paper:
- Hatef Otroshi Shahreza, Vedrana Krivokuća Hahn, and Sébastien Marcel. "MLP-hash: Protecting face templates via hashing of randomized multi-layer perceptron" In Proc of the European Signal Processing Conference (EUSIPCO), 2023.
Note: If you use this implementation, please cite the above paper:
"""
import numpy
import scipy.spatial
def nonlinear_funcftion( in_vector):
return numpy.where(in_vector<0,0,in_vector)
def protect(feat_vec, user_seed, mlph_layers=[512,512,512]):
""" Creates a MLPHash by projecting the input biometric feature vector onto a set of randomly generated basis vectors and then binarising the resulting vector
**Parameters:**
feat_vec (array): The extracted fingervein feture vector
mlph_layers (list of int): The desired lengths (i.e., hidden layers) of the MLP
user_seed (int): The seed used to generate the user's specific random projection matrix
**Returns:**
mlphash (array): The resulting MLPHash, which is a protected, binary representation of the input feature vector
"""
numpy.random.seed(user_seed) # re-seed the random number generator according to the user's specific seed
mlphash = feat_vec
for i, _len in enumerate(mlph_layers):
rand_mat = numpy.random.rand(len(mlphash), _len) # generate matrix of random values from uniform distribution over [0, 1]
orth_mat, _ = numpy.linalg.qr(rand_mat, mode='reduced') # orthonormalise columns of random matrix, mode='reduced' returns orth_mat with size len(feat_vec) x mlphash
mlphash = numpy.dot(mlphash, orth_mat)
mlphash = nonlinear_funcftion(mlphash)
thresh = numpy.mean(mlphash) # threshold by which to binarise vector of dot products to generate final BioHash
mlphash = numpy.where(mlphash > thresh, 1, 0)
return mlphash
def score( model, probe):
"""score(model, probe) -> score
This function will compute the Hamming distance between the given model and probe MLPHashes.
**Parameters:**
model : object
The model MLPHash to compare the probe MLPHash with.
probe : object
The probe MLPHash to compare the model MLPHash with.
**Returns:**
score : float
The Hamming distance between ``model`` and ``probe``.
Higher values define higher similarities.
"""
return scipy.spatial.distance.hamming(model, probe) * -1
\ No newline at end of file
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
import torch
from sklearn.base import TransformerMixin, BaseEstimator
from sklearn.utils import check_array
import numpy as np
import imp
import os
from bob.extension.download import get_file
class PyTorchModel(TransformerMixin, BaseEstimator):
"""
Base Transformer using pytorch models
Parameters
----------
checkpoint_path: str
Path containing the checkpoint
config:
Path containing some configuration file (e.g. .json, .prototxt)
preprocessor:
A function that will transform the data right before forward. The default transformation is `X/255`
"""
def __init__(
self,
checkpoint_path=None,
config=None,
preprocessor=lambda x: (x - 127.5) / 128.0,
device='cpu',
image_dim = 112,
**kwargs
):
super().__init__(**kwargs)
self.checkpoint_path = checkpoint_path
self.config = config
self.model = None
self.preprocessor_ = preprocessor
self.device = device
self.image_dim= image_dim
def preprocessor(self, X):
X = self.preprocessor_(X)
if X.size(2) != self.image_dim:
X = torch.nn.functional.interpolate(X, mode='bilinear', size=(self.image_dim, self.image_dim), align_corners=False)
return X
def transform(self, X):
"""__call__(image) -> feature
Extracts the features from the given image.
**Parameters:**
image : 2D :py:class:`numpy.ndarray` (floats)
The image to extract the features from.
**Returns:**
feature : 2D or 3D :py:class:`numpy.ndarray` (floats)
The list of features extracted from the image.
"""
if self.model is None:
self._load_model()
self.model.eval()
self.model.to(self.device)
for param in self.model.parameters():
param.requires_grad=False
# X = check_array(X, allow_nd=True)
# X = torch.Tensor(X)
X = self.preprocessor(X)
return self.model(X)#.detach().numpy()
def __getstate__(self):
# Handling unpicklable objects
d = self.__dict__.copy()
d["model"] = None
return d
def _more_tags(self):
return {"stateless": True, "requires_fit": False}
def to(self,device):
self.device=device
if self.model !=None:
self.model.to(self.device)
def _get_iresnet_file():
urls = [
"https://www.idiap.ch/software/bob/data/bob/bob.bio.face/master/pytorch/iresnet-91a5de61.tar.gz",
"http://www.idiap.ch/software/bob/data/bob/bob.bio.face/master/pytorch/iresnet-91a5de61.tar.gz",
]
return get_file(
"iresnet-91a5de61.tar.gz",
urls,
cache_subdir="data/pytorch/iresnet-91a5de61/",
file_hash="3976c0a539811d888ef5b6217e5de425",
extract=True,
)
class IResnet100(PyTorchModel):
"""
ArcFace model (RESNET 100) from Insightface ported to pytorch
"""
def __init__(self,
preprocessor=lambda x: (x - 127.5) / 128.0,
device='cpu'
):
self.device = device
filename = _get_iresnet_file()
path = os.path.dirname(filename)
config = os.path.join(path, "iresnet.py")
checkpoint_path = os.path.join(path, "iresnet100-73e07ba7.pth")
super(IResnet100, self).__init__(
checkpoint_path, config, device=device
)
def _load_model(self):
model = imp.load_source("module", self.config).iresnet100(self.checkpoint_path)
self.model = model
class IResnet100Elastic(PyTorchModel):
"""
ElasticFace model
"""
def __init__(self,
preprocessor=lambda x: (x - 127.5) / 128.0,
device='cpu'
):
self.device = device
urls = [
"https://www.idiap.ch/software/bob/data/bob/bob.bio.face/master/pytorch/iresnet100-elastic.tar.gz",
"http://www.idiap.ch/software/bob/data/bob/bob.bio.face/master/pytorch/iresnet100-elastic.tar.gz",
]
filename= get_file(
"iresnet100-elastic.tar.gz",
urls,
cache_subdir="data/pytorch/iresnet100-elastic/",
file_hash="0ac36db3f0f94930993afdb27faa4f02",
extract=True,
)
path = os.path.dirname(filename)
config = os.path.join(path, "iresnet.py")
checkpoint_path = os.path.join(path, "iresnet100-elastic.pt")
super(IResnet100Elastic, self).__init__(
checkpoint_path, config, device=device, preprocessor=preprocessor,
)
def _load_model(self):
model = imp.load_source("module", self.config).iresnet100(self.checkpoint_path)
self.model = model
from bob.bio.facexzoo.backbones import FaceXZooModelFactory
class FaceXZooModel(PyTorchModel):
"""
FaceXZoo models
"""
def __init__(
self,
preprocessor=lambda x: (x - 127.5) / 128.0,
device=None,
arch="MobileFaceNet",
head='MV-Softmax',
**kwargs,
):
self.arch = arch
self.head = head
_model = FaceXZooModelFactory(self.arch, self.head)
filename = _model.get_facexzoo_file()
checkpoint_name = _model.get_checkpoint_name()
config = None
path = os.path.dirname(filename)
checkpoint_path = filename#os.path.join(path, self.arch + ".pt")
if arch == "SwinTransformer_S" or arch == "SwinTransformer_T":
image_dim = 224
else:
image_dim = 112
super(FaceXZooModel, self).__init__(
checkpoint_path,
config,
preprocessor=preprocessor,
device=device,
image_dim = image_dim,
**kwargs,
)
def _load_model(self):
_model = FaceXZooModelFactory(self.arch, self.head)
self.model = _model.get_model()
model_dict = self.model.state_dict()
pretrained_dict = torch.load(
self.checkpoint_path, map_location=torch.device("cpu")
)["state_dict"]
pretrained_dict_keys = pretrained_dict.keys()
model_dict_keys = model_dict.keys()
new_pretrained_dict = {}
for k in model_dict:
new_pretrained_dict[k] = pretrained_dict["backbone." + k]
model_dict.update(new_pretrained_dict)
self.model.load_state_dict(model_dict)
def get_FaceRecognition_transformer(FR_system='ArcFace', device='cpu'):
if FR_system == 'ArcFace':
FaceRecognition_transformer = IResnet100(device=device)
elif FR_system == 'ElasticFace':
FaceRecognition_transformer = IResnet100Elastic(device=device)
elif FR_system == 'AttentionNet92':
FaceRecognition_transformer = FaceXZooModel(arch="AttentionNet92", device=device)
elif FR_system == 'HRNet':
FaceRecognition_transformer = FaceXZooModel(arch="HRNet", device=device)
elif FR_system == 'RepVGG_B1':
FaceRecognition_transformer = FaceXZooModel(arch="RepVGG_B1", device=device)
elif FR_system == 'SwinTransformer_S':
FaceRecognition_transformer = FaceXZooModel(arch="SwinTransformer_S", device=device)
else:
print(f"[FaceIDLoss] {FR_system} is not defined!")
return FaceRecognition_transformer
class ID_Loss:
def __init__(self, FR_system='ArcFace', device='cpu' ):
self.FaceRecognition_transformer= get_FaceRecognition_transformer(FR_system=FR_system, device=device)
def __call__(self, img1,img2):
embedding1 = self.FaceRecognition_transformer.transform(img1*255.0) # Note: input img should be in (0,1)
embedding2 = self.FaceRecognition_transformer.transform(img2*255.0) # Note: input img should be in (0,1)
return torch.nn.L1Loss()(embedding1,embedding2)-torch.nn.CosineSimilarity()(embedding1,embedding2).mean()
\ No newline at end of file
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
import torch
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np
from math import exp
def gaussian(window_size, sigma):
gauss = torch.Tensor([exp(-(x - window_size//2)**2/float(2*sigma**2)) for x in range(window_size)])
return gauss/gauss.sum()
def create_window(window_size, channel):
_1D_window = gaussian(window_size, 1.5).unsqueeze(1)
_2D_window = _1D_window.mm(_1D_window.t()).float().unsqueeze(0).unsqueeze(0)
window = Variable(_2D_window.expand(channel, 1, window_size, window_size).contiguous())
return window
def _ssim(img1, img2, window, window_size, channel, size_average = True):
mu1 = F.conv2d(img1, window, padding = window_size//2, groups = channel)
mu2 = F.conv2d(img2, window, padding = window_size//2, groups = channel)
mu1_sq = mu1.pow(2)
mu2_sq = mu2.pow(2)
mu1_mu2 = mu1*mu2
sigma1_sq = F.conv2d(img1*img1, window, padding = window_size//2, groups = channel) - mu1_sq
sigma2_sq = F.conv2d(img2*img2, window, padding = window_size//2, groups = channel) - mu2_sq
sigma12 = F.conv2d(img1*img2, window, padding = window_size//2, groups = channel) - mu1_mu2
C1 = 0.01**2
C2 = 0.03**2
ssim_map = ((2*mu1_mu2 + C1)*(2*sigma12 + C2))/((mu1_sq + mu2_sq + C1)*(sigma1_sq + sigma2_sq + C2))
if size_average:
return ssim_map.mean()
else:
return ssim_map.mean(1).mean(1).mean(1)
def ssim(img1, img2, window_size = 11, size_average = True):
(_, channel, _, _) = img1.size()
window = create_window(window_size, channel)
if img1.is_cuda:
window = window.cuda(img1.get_device())
window = window.type_as(img1)
return _ssim(img1, img2, window, window_size, channel, size_average)
class SSIM_Loss(torch.nn.Module):
def __init__(self, window_size = 11, size_average = True):
super(SSIM_Loss, self).__init__()
self.window_size = window_size
self.size_average = size_average
self.channel = 1
self.window = create_window(window_size, self.channel)
def forward(self, img1, img2):
(_, channel, _, _) = img1.size()
if channel == self.channel and self.window.data.type() == img1.data.type():
window = self.window
else:
window = create_window(self.window_size, channel)
if img1.is_cuda:
window = window.cuda(img1.get_device())
window = window.type_as(img1)
self.window = window
self.channel = channel
return (1-_ssim(img1, img2, window, self.window_size, channel, self.size_average))*0.5
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
import numpy
def find_q_levels(all_data, n_quantization_levels):
'''
Parametrs:
all_data: array
n_quantization_levels: int
Return:
quantization_levels: list
quantization levels
'''
all_data = all_data.flatten()
samples_per_bin = int(all_data.shape[0]/n_quantization_levels)
edge_samples = (numpy.arange(n_quantization_levels-1) + 1 ) * samples_per_bin
all_sorted_data = numpy.sort(all_data)
quantization_levels = all_sorted_data[edge_samples]
return quantization_levels
def quantize_f(f, quantization_levels):
'''
Parametrs:
f: float
quantization_levels: list
Return:
level: int
quantization level for f
'''
level = len(quantization_levels) - 1
for l in range(len(quantization_levels)):
if f<quantization_levels[l]:
level = l
break
return level
def quantize_vector(data, quantization_levels):
quantized_data = []
for i,f in enumerate(data):
q_level_f = quantize_f(f, quantization_levels)
quantized_data.append(q_level_f)
return numpy.array(quantized_data, dtype='int')
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
'''
Note: If you use this implementation, please cite the following paper:
- Hatef Otroshi Shahreza and Sébastien Marcel. "Breaking Template
Protection: Reconstruction of Face Images from Protected Facial
Templates", IEEE 18th International Conference on Automatic Face
and Gesture Recognition (FG), 2024.
'''
import os,sys
sys.path.append(os.getcwd())
import torch
from torch.utils.data import Dataset
import bob.io.base
import glob
import random
import numpy as np
import cv2
import pickle
import argparse
parser = argparse.ArgumentParser(description='Train face reconstruction network from complete facial embeddings')
parser.add_argument('--FR_system', metavar='<FR_system>', type= str, default='ArcFace',
help='ArcFace/ElasticFace')
parser.add_argument('--FR_loss', metavar='<FR_loss>', type= str, default='ArcFace',
help='ArcFace/ElasticFace')
parser.add_argument('--btp', metavar='<btp>', type= str, default='BioHashing',
help='btp scheme')
parser.add_argument('--user_seed', metavar='<user_seed>', type= int, default=0,
help='random seed')
args = parser.parse_args()
print(f'*** Train TI \n against {args.FR_system} FR\n protected with {args.btp}\n {args.FR_system} for loss\n [seed: {args.user_seed}]')
seed=args.user_seed
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print("************ NOTE: The torch device is:", device)
#=================== import Dataset ======================
from src.Dataset import MyDataset
from torch.utils.data import DataLoader
training_dataset = MyDataset(dataset_dir = f'datasets/{args.FR_system}', train=True, user_seed=args.user_seed, btp = args.btp)
testing_dataset = MyDataset(dataset_dir = f'datasets/{args.FR_system}', train=False, user_seed=args.user_seed, btp = args.btp)
if args.btp=='HE_BFV':
os.makedirs(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/',exist_ok=True)
path = f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/quantization_levels.npy'
training_dataset.find_quantization_levels_and_save(path, n_quantization_levels=8)
testing_dataset.load_quantization_levels(path)
train_dataloader = DataLoader(training_dataset, batch_size=32, num_workers=8, shuffle=True, drop_last=True)
test_dataloader = DataLoader(testing_dataset, batch_size=32, num_workers=8, shuffle=False, drop_last=True)
#========================================================
#=================== import Network =====================
length_of_embedding= training_dataset.len_protected # embedding.size(1)
from src.Network import Generator
model_Generator = Generator(length_of_embedding=length_of_embedding)
model_Generator.to(device)
#========================================================
#=================== import Loss ========================
# ***** SSIM_Loss
from src.loss.SSIMLoss import SSIM_Loss
ssim_loss = SSIM_Loss()
ssim_loss.to(device)
# ***** ID_loss
from src.loss.FaceIDLoss import ID_Loss
ID_loss = ID_Loss(FR_system=args.FR_system, device=device)
# ***** Other losses
MAE_loss = torch.nn.L1Loss()
#========================================================
#=================== Optimizers =========================
# ***** optimizer_Generator
optimizer_Generator = torch.optim.Adam(model_Generator.parameters(), lr=1e-3)
scheduler_Generator = torch.optim.lr_scheduler.StepLR(optimizer_Generator, step_size=10, gamma=0.5)
#========================================================
#=================== Save models and logs ===============
os.makedirs(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/models',exist_ok=True)
os.makedirs(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/Generated_images',exist_ok=True)
os.makedirs(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/logs_train',exist_ok=True)
with open(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/logs_train/generator.csv','w') as f:
f.write("epoch,MAE_loss_Gen,ID_loss_Gen,ssim_loss_Gen_test,total_loss_Gen\n")
#========================================================
#=================== Train ==============================
num_epochs=45
from tqdm import tqdm
for epoch in tqdm(range(num_epochs)):
iteration=0
print(f'epoch: {epoch}, \t learning rate: {optimizer_Generator.param_groups[0]["lr"]}')
model_Generator.train()
for embedding, real_image in train_dataloader:
embedding = embedding.to(device)
real_image = real_image.to(device)
# ==================forward==================
generated_image = model_Generator(embedding)
MAE = MAE_loss(generated_image, real_image)
ID = ID_loss(generated_image, real_image)
ssim = ssim_loss(generated_image, real_image)
total_loss_Generato = MAE + 0.75*ssim + 0.025*ID
# ==================backward=================
optimizer_Generator.zero_grad()
total_loss_Generato.backward()
optimizer_Generator.step()
# ==================log======================
# iteration +=1
# if iteration % 200 == 0:
# # model_Generator.eval()
# #print(f'epoch:{epoch+1} \t, iteration: {iteration}, \t total_loss:{total_loss_Generato.data.item()}')
# with open(f'training_checkpoints/{args.FR_system}/training_files-{0.0}-{seed}-{perc_ommited_features}-{seed}/logs_train/log.txt','a') as f:
# f.write(f'epoch:{epoch+1}, \t iteration: {iteration}, \t total_loss:{total_loss_Generato.data.item()}\n')
# pass
# ******************** Eval Genrator ********************
model_Generator.eval()
MAE_loss_Gen_test = ID_loss_Gen_test = ssim_loss_Gen_test = total_loss_Gen_test = 0
iteration = 0
for embedding, real_image in test_dataloader:
embedding = embedding.to(device)
real_image = real_image.to(device)
iteration +=1
# ==================forward==================
with torch.no_grad():
generated_image = model_Generator(embedding)
MAE = MAE_loss(generated_image, real_image)
ID = ID_loss(generated_image, real_image)
ssim = ssim_loss(generated_image, real_image)
total_loss_Generato = MAE + 0.75*ssim + 0.025*ID
####
MAE_loss_Gen_test += MAE.item()
ID_loss_Gen_test += ID.item()
ssim_loss_Gen_test += ssim.item()
total_loss_Gen_test += total_loss_Generato.item()
with open(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/logs_train/generator.csv','a') as f:
f.write(f"{epoch+1}, {MAE_loss_Gen_test/iteration}, {ID_loss_Gen_test/iteration}, {ssim_loss_Gen_test/iteration}, {total_loss_Gen_test/iteration}\n")
# Update oprimizer_Generator lr
scheduler_Generator.step()
# *******************************************************
# Generate and save images
real_image=real_image.cpu()
for i in range(real_image.size(0)):
os.makedirs(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/Generated_images/{i}', exist_ok=True)
img = real_image[i].squeeze()
im = (img.numpy().transpose(1,2,0)*255).astype(int)
cv2.imwrite(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/Generated_images/{i}/real_image.jpg',np.array([im[:,:,2],im[:,:,1],im[:,:,0]]).transpose(1,2,0))
generated_image = model_Generator(embedding).detach().cpu()
for i in range(generated_image.size(0)):
img = generated_image[i].squeeze()
im = (img.numpy().transpose(1,2,0)*255).astype(int)
cv2.imwrite(f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}/Generated_images/{i}/epoch_{epoch+1}.jpg',np.array([im[:,:,2],im[:,:,1],im[:,:,0]]).transpose(1,2,0))
# Save model_Generator
torch.save(model_Generator.state_dict(), f'training_checkpoints/{args.FR_system}-{args.btp}-{args.FR_loss}_loss/training_files-{seed}'+'/models/Generator_{}.pth'.format(epoch+1))
#========================================================
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
# see https://docs.python.org/3/library/pkgutil.html
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
# SPDX-FileCopyrightText: Copyright © 2024 Idiap Research Institute <contact@idiap.ch>
# SPDX-FileContributor: Hatef OTROSHI <hatef.otroshi@idiap.ch>
# SPDX-License-Identifier: MIT
# see https://docs.python.org/3/library/pkgutil.html
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
This diff is collapsed.
Part of the scripts are taken from FaceXZoo reposiroty which is licensed under Apache 2.
For more details please check the [LICENSE](LICENSE.txt) file.
\ No newline at end of file
"""
@author: Jun Wang
@date: 20201019
@contact: jun21wangustc@gmail.com
"""
# based on:
# https://github.com/tengshaofeng/ResidualAttentionNetwork-pytorch/tree/master/Residual-Attention-Network/model
import torch
import torch.nn as nn
from torch.nn import init
import functools
from torch.autograd import Variable
import numpy as np
class Flatten(nn.Module):
def forward(self, x):
return x.reshape(x.size(0), -1)
class ResidualBlock(nn.Module):
def __init__(self, input_channels, output_channels, stride=1):
super(ResidualBlock, self).__init__()
self.input_channels = input_channels
self.output_channels = output_channels
self.stride = stride
self.bn1 = nn.BatchNorm2d(input_channels)
self.relu = nn.ReLU(inplace=True)
self.conv1 = nn.Conv2d(input_channels, output_channels//4, 1, 1, bias = False)
self.bn2 = nn.BatchNorm2d(output_channels//4)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(output_channels//4, output_channels//4, 3, stride, padding = 1, bias = False)
self.bn3 = nn.BatchNorm2d(output_channels//4)
self.relu = nn.ReLU(inplace=True)
self.conv3 = nn.Conv2d(output_channels//4, output_channels, 1, 1, bias = False)
self.conv4 = nn.Conv2d(input_channels, output_channels , 1, stride, bias = False)
def forward(self, x):
residual = x
out = self.bn1(x)
out1 = self.relu(out)
out = self.conv1(out1)
out = self.bn2(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn3(out)
out = self.relu(out)
out = self.conv3(out)
if (self.input_channels != self.output_channels) or (self.stride !=1 ):
residual = self.conv4(out1)
out += residual
return out
class AttentionModule_stage1(nn.Module):
# input size is 56*56
def __init__(self, in_channels, out_channels, size1=(56, 56), size2=(28, 28), size3=(14, 14)):
super(AttentionModule_stage1, self).__init__()
self.first_residual_blocks = ResidualBlock(in_channels, out_channels)
self.trunk_branches = nn.Sequential(
ResidualBlock(in_channels, out_channels),
ResidualBlock(in_channels, out_channels)
)
self.mpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.softmax1_blocks = ResidualBlock(in_channels, out_channels)
self.skip1_connection_residual_block = ResidualBlock(in_channels, out_channels)
self.mpool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.softmax2_blocks = ResidualBlock(in_channels, out_channels)
self.skip2_connection_residual_block = ResidualBlock(in_channels, out_channels)
self.mpool3 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.softmax3_blocks = nn.Sequential(
ResidualBlock(in_channels, out_channels),
ResidualBlock(in_channels, out_channels)
)
self.interpolation3 = nn.UpsamplingBilinear2d(size=size3)
self.softmax4_blocks = ResidualBlock(in_channels, out_channels)
self.interpolation2 = nn.UpsamplingBilinear2d(size=size2)
self.softmax5_blocks = ResidualBlock(in_channels, out_channels)
self.interpolation1 = nn.UpsamplingBilinear2d(size=size1)
self.softmax6_blocks = nn.Sequential(
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels , kernel_size = 1, stride = 1, bias = False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels , kernel_size = 1, stride = 1, bias = False),
nn.Sigmoid()
)
self.last_blocks = ResidualBlock(in_channels, out_channels)
def forward(self, x):
x = self.first_residual_blocks(x)
out_trunk = self.trunk_branches(x)
out_mpool1 = self.mpool1(x)
out_softmax1 = self.softmax1_blocks(out_mpool1)
out_skip1_connection = self.skip1_connection_residual_block(out_softmax1)
out_mpool2 = self.mpool2(out_softmax1)
out_softmax2 = self.softmax2_blocks(out_mpool2)
out_skip2_connection = self.skip2_connection_residual_block(out_softmax2)
out_mpool3 = self.mpool3(out_softmax2)
out_softmax3 = self.softmax3_blocks(out_mpool3)
#
out_interp3 = self.interpolation3(out_softmax3) + out_softmax2
# print(out_skip2_connection.data)
# print(out_interp3.data)
out = out_interp3 + out_skip2_connection
out_softmax4 = self.softmax4_blocks(out)
out_interp2 = self.interpolation2(out_softmax4) + out_softmax1
out = out_interp2 + out_skip1_connection
out_softmax5 = self.softmax5_blocks(out)
out_interp1 = self.interpolation1(out_softmax5) + out_trunk
out_softmax6 = self.softmax6_blocks(out_interp1)
out = (1 + out_softmax6) * out_trunk
out_last = self.last_blocks(out)
return out_last
class AttentionModule_stage2(nn.Module):
# input image size is 28*28
def __init__(self, in_channels, out_channels, size1=(28, 28), size2=(14, 14)):
super(AttentionModule_stage2, self).__init__()
self.first_residual_blocks = ResidualBlock(in_channels, out_channels)
self.trunk_branches = nn.Sequential(
ResidualBlock(in_channels, out_channels),
ResidualBlock(in_channels, out_channels)
)
self.mpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.softmax1_blocks = ResidualBlock(in_channels, out_channels)
self.skip1_connection_residual_block = ResidualBlock(in_channels, out_channels)
self.mpool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.softmax2_blocks = nn.Sequential(
ResidualBlock(in_channels, out_channels),
ResidualBlock(in_channels, out_channels)
)
self.interpolation2 = nn.UpsamplingBilinear2d(size=size2)
self.softmax3_blocks = ResidualBlock(in_channels, out_channels)
self.interpolation1 = nn.UpsamplingBilinear2d(size=size1)
self.softmax4_blocks = nn.Sequential(
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=1, stride=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=1, stride=1, bias=False),
nn.Sigmoid()
)
self.last_blocks = ResidualBlock(in_channels, out_channels)
def forward(self, x):
x = self.first_residual_blocks(x)
out_trunk = self.trunk_branches(x)
out_mpool1 = self.mpool1(x)
out_softmax1 = self.softmax1_blocks(out_mpool1)
out_skip1_connection = self.skip1_connection_residual_block(out_softmax1)
out_mpool2 = self.mpool2(out_softmax1)
out_softmax2 = self.softmax2_blocks(out_mpool2)
out_interp2 = self.interpolation2(out_softmax2) + out_softmax1
# print(out_skip2_connection.data)
# print(out_interp3.data)
out = out_interp2 + out_skip1_connection
out_softmax3 = self.softmax3_blocks(out)
out_interp1 = self.interpolation1(out_softmax3) + out_trunk
out_softmax4 = self.softmax4_blocks(out_interp1)
out = (1 + out_softmax4) * out_trunk
out_last = self.last_blocks(out)
return out_last
class AttentionModule_stage3(nn.Module):
# input image size is 14*14
def __init__(self, in_channels, out_channels, size1=(14, 14)):
super(AttentionModule_stage3, self).__init__()
self.first_residual_blocks = ResidualBlock(in_channels, out_channels)
self.trunk_branches = nn.Sequential(
ResidualBlock(in_channels, out_channels),
ResidualBlock(in_channels, out_channels)
)
self.mpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.softmax1_blocks = nn.Sequential(
ResidualBlock(in_channels, out_channels),
ResidualBlock(in_channels, out_channels)
)
self.interpolation1 = nn.UpsamplingBilinear2d(size=size1)
self.softmax2_blocks = nn.Sequential(
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=1, stride=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=1, stride=1, bias=False),
nn.Sigmoid()
)
self.last_blocks = ResidualBlock(in_channels, out_channels)
def forward(self, x):
x = self.first_residual_blocks(x)
out_trunk = self.trunk_branches(x)
out_mpool1 = self.mpool1(x)
out_softmax1 = self.softmax1_blocks(out_mpool1)
out_interp1 = self.interpolation1(out_softmax1) + out_trunk
out_softmax2 = self.softmax2_blocks(out_interp1)
out = (1 + out_softmax2) * out_trunk
out_last = self.last_blocks(out)
return out_last
class ResidualAttentionNet(nn.Module):
def __init__(self, stage1_modules, stage2_modules, stage3_modules, feat_dim, out_h, out_w):
super(ResidualAttentionNet, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias = False),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True)
)
attention_modules = []
attention_modules.append(ResidualBlock(64, 256))
# stage 1
for i in range(stage1_modules):
attention_modules.append(AttentionModule_stage1(256, 256))
attention_modules.append(ResidualBlock(256, 512, 2))
# stage2
for i in range(stage2_modules):
attention_modules.append(AttentionModule_stage2(512, 512))
attention_modules.append(ResidualBlock(512, 1024, 2))
# stage3
for i in range(stage3_modules):
attention_modules.append(AttentionModule_stage3(1024, 1024))
# final residual
attention_modules.append(ResidualBlock(1024, 2048, 2))
attention_modules.append(ResidualBlock(2048, 2048))
attention_modules.append(ResidualBlock(2048, 2048))
self.attention_body = nn.Sequential(*attention_modules)
# output layer
self.output_layer = nn.Sequential(
Flatten(),
nn.Linear(2048 * out_h * out_w, feat_dim, False),
nn.BatchNorm1d(feat_dim))
def forward(self, x):
out = self.conv1(x)
out = self.attention_body(out)
out = self.output_layer(out)
return out
This diff is collapsed.
"""
@author: Jun Wang
@date: 20210121
@contact: jun21wangustc@gmail.com
"""
# based on:
# https://github.com/huawei-noah/ghostnet/blob/master/ghostnet_pytorch/ghostnet.py
# 2020.06.09-Changed for building GhostNet
# Huawei Technologies Co., Ltd. <foss@huawei.com>
"""
Creates a GhostNet Model as defined in:
GhostNet: More Features from Cheap Operations By Kai Han, Yunhe Wang, Qi Tian, Jianyuan Guo, Chunjing Xu, Chang Xu.
https://arxiv.org/abs/1911.11907
Modified from https://github.com/d-li14/mobilenetv3.pytorch and https://github.com/rwightman/pytorch-image-models
"""
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn import Sequential, BatchNorm2d, Dropout, Module, Linear, BatchNorm1d
__all__ = ['ghost_net']
class Flatten(Module):
def forward(self, input):
return input.reshape(input.size(0), -1)
def _make_divisible(v, divisor, min_value=None):
"""
This function is taken from the original tf repo.
It ensures that all layers have a channel number that is divisible by 8
It can be seen here:
https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
"""
if min_value is None:
min_value = divisor
new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
# Make sure that round down does not go down by more than 10%.
if new_v < 0.9 * v:
new_v += divisor
return new_v
def hard_sigmoid(x, inplace: bool = False):
if inplace:
return x.add_(3.).clamp_(0., 6.).div_(6.)
else:
return F.relu6(x + 3.) / 6.
class SqueezeExcite(nn.Module):
def __init__(self, in_chs, se_ratio=0.25, reduced_base_chs=None,
act_layer=nn.ReLU, gate_fn=hard_sigmoid, divisor=4, **_):
super(SqueezeExcite, self).__init__()
self.gate_fn = gate_fn
reduced_chs = _make_divisible((reduced_base_chs or in_chs) * se_ratio, divisor)
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.conv_reduce = nn.Conv2d(in_chs, reduced_chs, 1, bias=True)
self.act1 = act_layer(inplace=True)
self.conv_expand = nn.Conv2d(reduced_chs, in_chs, 1, bias=True)
def forward(self, x):
x_se = self.avg_pool(x)
x_se = self.conv_reduce(x_se)
x_se = self.act1(x_se)
x_se = self.conv_expand(x_se)
x = x * self.gate_fn(x_se)
return x
class ConvBnAct(nn.Module):
def __init__(self, in_chs, out_chs, kernel_size,
stride=1, act_layer=nn.ReLU):
super(ConvBnAct, self).__init__()
self.conv = nn.Conv2d(in_chs, out_chs, kernel_size, stride, kernel_size//2, bias=False)
self.bn1 = nn.BatchNorm2d(out_chs)
self.act1 = act_layer(inplace=True)
def forward(self, x):
x = self.conv(x)
x = self.bn1(x)
x = self.act1(x)
return x
class GhostModule(nn.Module):
def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):
super(GhostModule, self).__init__()
self.oup = oup
init_channels = math.ceil(oup / ratio)
new_channels = init_channels*(ratio-1)
self.primary_conv = nn.Sequential(
nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False),
nn.BatchNorm2d(init_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)
self.cheap_operation = nn.Sequential(
nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False),
nn.BatchNorm2d(new_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)
def forward(self, x):
x1 = self.primary_conv(x)
x2 = self.cheap_operation(x1)
out = torch.cat([x1,x2], dim=1)
return out[:,:self.oup,:,:]
class GhostBottleneck(nn.Module):
""" Ghost bottleneck w/ optional SE"""
def __init__(self, in_chs, mid_chs, out_chs, dw_kernel_size=3,
stride=1, act_layer=nn.ReLU, se_ratio=0.):
super(GhostBottleneck, self).__init__()
has_se = se_ratio is not None and se_ratio > 0.
self.stride = stride
# Point-wise expansion
self.ghost1 = GhostModule(in_chs, mid_chs, relu=True)
# Depth-wise convolution
if self.stride > 1:
self.conv_dw = nn.Conv2d(mid_chs, mid_chs, dw_kernel_size, stride=stride,
padding=(dw_kernel_size-1)//2,
groups=mid_chs, bias=False)
self.bn_dw = nn.BatchNorm2d(mid_chs)
# Squeeze-and-excitation
if has_se:
self.se = SqueezeExcite(mid_chs, se_ratio=se_ratio)
else:
self.se = None
# Point-wise linear projection
self.ghost2 = GhostModule(mid_chs, out_chs, relu=False)
# shortcut
if (in_chs == out_chs and self.stride == 1):
self.shortcut = nn.Sequential()
else:
self.shortcut = nn.Sequential(
nn.Conv2d(in_chs, in_chs, dw_kernel_size, stride=stride,
padding=(dw_kernel_size-1)//2, groups=in_chs, bias=False),
nn.BatchNorm2d(in_chs),
nn.Conv2d(in_chs, out_chs, 1, stride=1, padding=0, bias=False),
nn.BatchNorm2d(out_chs),
)
def forward(self, x):
residual = x
# 1st ghost bottleneck
x = self.ghost1(x)
# Depth-wise convolution
if self.stride > 1:
x = self.conv_dw(x)
x = self.bn_dw(x)
# Squeeze-and-excitation
if self.se is not None:
x = self.se(x)
# 2nd ghost bottleneck
x = self.ghost2(x)
x += self.shortcut(residual)
return x
class GhostNet(nn.Module):
def __init__(self, width=1.0, drop_ratio=0.2, feat_dim=512, out_h=7, out_w=7):
super(GhostNet, self).__init__()
# setting of inverted residual blocks
self.cfgs = [
# k, t, c, SE, s
# stage1
[[3, 16, 16, 0, 1]],
# stage2
[[3, 48, 24, 0, 2]],
[[3, 72, 24, 0, 1]],
# stage3
[[5, 72, 40, 0.25, 2]],
[[5, 120, 40, 0.25, 1]],
# stage4
[[3, 240, 80, 0, 2]],
[[3, 200, 80, 0, 1],
[3, 184, 80, 0, 1],
[3, 184, 80, 0, 1],
[3, 480, 112, 0.25, 1],
[3, 672, 112, 0.25, 1]
],
# stage5
[[5, 672, 160, 0.25, 2]],
[[5, 960, 160, 0, 1],
[5, 960, 160, 0.25, 1],
[5, 960, 160, 0, 1],
[5, 960, 160, 0.25, 1]
]
]
# building first layer
output_channel = _make_divisible(16 * width, 4)
#self.conv_stem = nn.Conv2d(3, output_channel, 3, 2, 1, bias=False)
self.conv_stem = nn.Conv2d(3, output_channel, 3, 1, 1, bias=False)
self.bn1 = nn.BatchNorm2d(output_channel)
self.act1 = nn.ReLU(inplace=True)
input_channel = output_channel
# building inverted residual blocks
stages = []
block = GhostBottleneck
for cfg in self.cfgs:
layers = []
for k, exp_size, c, se_ratio, s in cfg:
output_channel = _make_divisible(c * width, 4)
hidden_channel = _make_divisible(exp_size * width, 4)
layers.append(block(input_channel, hidden_channel, output_channel, k, s,
se_ratio=se_ratio))
input_channel = output_channel
stages.append(nn.Sequential(*layers))
output_channel = _make_divisible(exp_size * width, 4)
stages.append(nn.Sequential(ConvBnAct(input_channel, output_channel, 1)))
input_channel = output_channel
self.blocks = nn.Sequential(*stages)
self.output_layer = Sequential(BatchNorm2d(960),
Dropout(drop_ratio),
Flatten(),
Linear(960 * out_h * out_w, feat_dim), # for eye
BatchNorm1d(feat_dim))
def forward(self, x):
x = self.conv_stem(x)
x = self.bn1(x)
x = self.act1(x)
x = self.blocks(x)
x = self.output_layer(x)
return x
This diff is collapsed.
'''
implement Light CNN
@author: Alfred Xiang Wu
@date: 2017.07.04
'''
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
class Flatten(nn.Module):
def forward(self, input):
return input.reshape(input.size(0), -1)
class mfm(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1, type=1):
super(mfm, self).__init__()
self.out_channels = out_channels
if type == 1:
self.filter = nn.Conv2d(in_channels, 2*out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
else:
self.filter = nn.Linear(in_channels, 2*out_channels)
def forward(self, x):
x = self.filter(x)
out = torch.split(x, self.out_channels, 1)
return torch.max(out[0], out[1])
class group(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding):
super(group, self).__init__()
self.conv_a = mfm(in_channels, in_channels, 1, 1, 0)
self.conv = mfm(in_channels, out_channels, kernel_size, stride, padding)
def forward(self, x):
x = self.conv_a(x)
x = self.conv(x)
return x
class resblock(nn.Module):
def __init__(self, in_channels, out_channels):
super(resblock, self).__init__()
self.conv1 = mfm(in_channels, out_channels, kernel_size=3, stride=1, padding=1)
self.conv2 = mfm(in_channels, out_channels, kernel_size=3, stride=1, padding=1)
def forward(self, x):
res = x
out = self.conv1(x)
out = self.conv2(out)
out = out + res
return out
class network_9layers(nn.Module):
def __init__(self, num_classes=79077):
super(network_9layers, self).__init__()
self.features = nn.Sequential(
mfm(1, 48, 5, 1, 2),
nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True),
group(48, 96, 3, 1, 1),
nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True),
group(96, 192, 3, 1, 1),
nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True),
group(192, 128, 3, 1, 1),
group(128, 128, 3, 1, 1),
nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True),
)
self.fc1 = mfm(8*8*128, 256, type=0)
self.fc2 = nn.Linear(256, num_classes)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1)
x = self.fc1(x)
x = F.dropout(x, training=self.training)
out = self.fc2(x)
return out, x
class network_29layers(nn.Module):
def __init__(self, block, layers, num_classes=79077):
super(network_29layers, self).__init__()
#self.conv1 = mfm(1, 48, 5, 1, 2)
self.conv1 = mfm(3, 48, 5, 1, 2)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)
self.block1 = self._make_layer(block, layers[0], 48, 48)
self.group1 = group(48, 96, 3, 1, 1)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)
self.block2 = self._make_layer(block, layers[1], 96, 96)
self.group2 = group(96, 192, 3, 1, 1)
self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)
self.block3 = self._make_layer(block, layers[2], 192, 192)
self.group3 = group(192, 128, 3, 1, 1)
self.block4 = self._make_layer(block, layers[3], 128, 128)
self.group4 = group(128, 128, 3, 1, 1)
self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)
self.fc = mfm(8*8*128, 256, type=0)
self.fc2 = nn.Linear(256, num_classes)
def _make_layer(self, block, num_blocks, in_channels, out_channels):
layers = []
for i in range(0, num_blocks):
layers.append(block(in_channels, out_channels))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = self.pool1(x)
x = self.block1(x)
x = self.group1(x)
x = self.pool2(x)
x = self.block2(x)
x = self.group2(x)
x = self.pool3(x)
x = self.block3(x)
x = self.group3(x)
x = self.block4(x)
x = self.group4(x)
x = self.pool4(x)
x = x.view(x.size(0), -1)
fc = self.fc(x)
fc = F.dropout(fc, training=self.training)
out = self.fc2(fc)
return out, fc
class network_29layers_v2(nn.Module):
def __init__(self, block, layers, drop_ratio, out_h, out_w, feat_dim):
super(network_29layers_v2, self).__init__()
#self.conv1 = mfm(1, 48, 5, 1, 2)
self.conv1 = mfm(3, 48, 5, 1, 2)
self.block1 = self._make_layer(block, layers[0], 48, 48)
self.group1 = group(48, 96, 3, 1, 1)
self.block2 = self._make_layer(block, layers[1], 96, 96)
self.group2 = group(96, 192, 3, 1, 1)
self.block3 = self._make_layer(block, layers[2], 192, 192)
self.group3 = group(192, 128, 3, 1, 1)
self.block4 = self._make_layer(block, layers[3], 128, 128)
self.group4 = group(128, 128, 3, 1, 1)
#self.fc = nn.Linear(8*8*128, 256)
#self.fc2 = nn.Linear(256, num_classes, bias=False)
self.output_layer = nn.Sequential(nn.BatchNorm2d(128),
nn.Dropout(drop_ratio),
Flatten(),
nn.Linear(128 * out_h * out_w, feat_dim),
nn.BatchNorm1d(feat_dim))
def _make_layer(self, block, num_blocks, in_channels, out_channels):
layers = []
for i in range(0, num_blocks):
layers.append(block(in_channels, out_channels))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = F.max_pool2d(x, 2) + F.avg_pool2d(x, 2)
x = self.block1(x)
x = self.group1(x)
x = F.max_pool2d(x, 2) + F.avg_pool2d(x, 2)
x = self.block2(x)
x = self.group2(x)
x = F.max_pool2d(x, 2) + F.avg_pool2d(x, 2)
x = self.block3(x)
x = self.group3(x)
x = self.block4(x)
x = self.group4(x)
x = F.max_pool2d(x, 2) + F.avg_pool2d(x, 2) # 7*7
#x = x.view(x.size(0), -1)
#fc = self.fc(x)
#x = F.dropout(fc, training=self.training)
#out = self.fc2(x)
#return out, fc
x = self.output_layer(x)
return x
def LightCNN_9Layers(drop_ratio, out_h, out_w, feat_dim):
model = network_9layers(drop_ratio, out_h, out_w, feat_dim)
return model
def LightCNN_29Layers(drop_ratio, out_h, out_w, feat_dim):
model = network_29layers(resblock, [1, 2, 3, 4], drop_ratio, out_h, out_w, feat_dim)
return model
def LightCNN_29Layers_v2(drop_ratio, out_h, out_w, feat_dim):
model = network_29layers_v2(resblock, [1, 2, 3, 4], drop_ratio, out_h, out_w, feat_dim)
return model
def LightCNN(depth, drop_ratio, out_h, out_w, feat_dim):
if depth == 9:
return LightCNN_9Layers(drop_ratio, out_h, out_w, feat_dim)
elif depth == 29:
return LightCNN_29Layers_v2(drop_ratio, out_h, out_w, feat_dim)
"""
@author: Jun Wang
@date: 20201019
@contact: jun21wangustc@gmail.com
"""
# based on:
# https://github.com/TreB1eN/InsightFace_Pytorch/blob/master/model.py
from torch.nn import Linear, Conv2d, BatchNorm1d, BatchNorm2d, PReLU, Sequential, Module
import torch
class Flatten(Module):
def forward(self, input):
return input.view(input.size(0), -1)
class Conv_block(Module):
def __init__(self, in_c, out_c, kernel=(1, 1), stride=(1, 1), padding=(0, 0), groups=1):
super(Conv_block, self).__init__()
self.conv = Conv2d(in_c, out_channels=out_c, kernel_size=kernel, groups=groups, stride=stride, padding=padding, bias=False)
self.bn = BatchNorm2d(out_c)
self.prelu = PReLU(out_c)
def forward(self, x):
x = self.conv(x)
x = self.bn(x)
x = self.prelu(x)
return x
class Linear_block(Module):
def __init__(self, in_c, out_c, kernel=(1, 1), stride=(1, 1), padding=(0, 0), groups=1):
super(Linear_block, self).__init__()
self.conv = Conv2d(in_c, out_channels=out_c, kernel_size=kernel, groups=groups, stride=stride, padding=padding, bias=False)
self.bn = BatchNorm2d(out_c)
def forward(self, x):
x = self.conv(x)
x = self.bn(x)
return x
class Depth_Wise(Module):
def __init__(self, in_c, out_c, residual = False, kernel=(3, 3), stride=(2, 2), padding=(1, 1), groups=1):
super(Depth_Wise, self).__init__()
self.conv = Conv_block(in_c, out_c=groups, kernel=(1, 1), padding=(0, 0), stride=(1, 1))
self.conv_dw = Conv_block(groups, groups, groups=groups, kernel=kernel, padding=padding, stride=stride)
self.project = Linear_block(groups, out_c, kernel=(1, 1), padding=(0, 0), stride=(1, 1))
self.residual = residual
def forward(self, x):
if self.residual:
short_cut = x
x = self.conv(x)
x = self.conv_dw(x)
x = self.project(x)
if self.residual:
output = short_cut + x
else:
output = x
return output
class Residual(Module):
def __init__(self, c, num_block, groups, kernel=(3, 3), stride=(1, 1), padding=(1, 1)):
super(Residual, self).__init__()
modules = []
for _ in range(num_block):
modules.append(Depth_Wise(c, c, residual=True, kernel=kernel, padding=padding, stride=stride, groups=groups))
self.model = Sequential(*modules)
def forward(self, x):
return self.model(x)
class MobileFaceNet(Module):
def __init__(self, embedding_size, out_h, out_w):
super(MobileFaceNet, self).__init__()
self.conv1 = Conv_block(3, 64, kernel=(3, 3), stride=(2, 2), padding=(1, 1))
self.conv2_dw = Conv_block(64, 64, kernel=(3, 3), stride=(1, 1), padding=(1, 1), groups=64)
self.conv_23 = Depth_Wise(64, 64, kernel=(3, 3), stride=(2, 2), padding=(1, 1), groups=128)
self.conv_3 = Residual(64, num_block=4, groups=128, kernel=(3, 3), stride=(1, 1), padding=(1, 1))
self.conv_34 = Depth_Wise(64, 128, kernel=(3, 3), stride=(2, 2), padding=(1, 1), groups=256)
self.conv_4 = Residual(128, num_block=6, groups=256, kernel=(3, 3), stride=(1, 1), padding=(1, 1))
self.conv_45 = Depth_Wise(128, 128, kernel=(3, 3), stride=(2, 2), padding=(1, 1), groups=512)
self.conv_5 = Residual(128, num_block=2, groups=256, kernel=(3, 3), stride=(1, 1), padding=(1, 1))
self.conv_6_sep = Conv_block(128, 512, kernel=(1, 1), stride=(1, 1), padding=(0, 0))
#self.conv_6_dw = Linear_block(512, 512, groups=512, kernel=(7,7), stride=(1, 1), padding=(0, 0))
#self.conv_6_dw = Linear_block(512, 512, groups=512, kernel=(4,7), stride=(1, 1), padding=(0, 0))
self.conv_6_dw = Linear_block(512, 512, groups=512, kernel=(out_h, out_w), stride=(1, 1), padding=(0, 0))
self.conv_6_flatten = Flatten()
self.linear = Linear(512, embedding_size, bias=False)
self.bn = BatchNorm1d(embedding_size)
def forward(self, x):
out = self.conv1(x)
out = self.conv2_dw(out)
out = self.conv_23(out)
out = self.conv_3(out)
out = self.conv_34(out)
out = self.conv_4(out)
out = self.conv_45(out)
out = self.conv_5(out)
out = self.conv_6_sep(out)
out = self.conv_6_dw(out)
out = self.conv_6_flatten(out)
out = self.linear(out)
out = self.bn(out)
return out
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment