0%

FaceXray论文阅读

Face X-ray实验

1 流程图及公式

1 流程图

2 公式1 混合

2.1 点积不是矩阵乘法

2.2 IF是前脸

2.3 IB是背景

2.4 M 是划定被操纵区域的面具,每个像素点的值被限制在0~1

2.5 混合前要colortransfor!

3 公式2

微信截图_20231108211101

具体实验步骤

使用HRNet网络

将来自所有四种不同分辨率的表示拼接到相同大小的 64x64

经过一个输出通道为 1x1 卷积层

一个输出大小为 256x256 的双线性上采样层

一个 sigmoid 函数。

训练过程

batch_size设置为32

总迭代轮数设置为200,000

热启动

前50,000次迭代用ImgNet训练HRNet(可以直接找现成的权重)

后150,000微调,用bi数据集

学习率一开始设置成0.002,使用Adam优化器,在最后的 50,000 次迭代中线性衰减为 0。

For the fully convolutional neural network NNb in our framework, we adopt the recent ad- vanced neural network architecture, i.e., HRNet [42, 43], and then concatenate representations from all four different resolutions to the same size 64 ⇥ 64, followed by a 1 ⇥ 1 convolutional layer with one output channel, a bilinear up- sampling layer with 256 ⇥ 256 output size and a sigmoid function. In the training process, the batch size is set to 32 and the total number of iterations is set to 200, 000. To ease the training process of our framework, we warm start the re- maining layers with fixed ImageNet pre-trained HRNet for the first 50, 000 iterations and then finetune all layers to- gether for the rest 150, 000 iterations. The learning rate is set as 0.0002 using Adam [20] optimizer at first and then is linearly decayed to 0 for the last 50, 000 iterations.

2 具体步骤

json文件读取

首先要产生BI数据集

将数据集保存到json文件中,json文件是以字典形式存储

1
2
3
4
with open("pre_computed_landmarks.json", "r") as f:
self.landmarks_record_dic = json.load(f)
for k, v in self.landmarks_record_dic.items():
landmarks_record_dic[k] = np.array(v)

图片读取与存取

利用skimage中io读取图片

1
2
from skimage import io
face_img = io.imread(background_face_path)

skimage.io.imread 返回的 NumPy 数组的默认格式是 HWC(Height, Width, Channels),即图像的高度、宽度和通道信息,通道顺序是RGB

以张量形式读取图片

1
read_image

图片对象与数组对象的转换

numpy

1
2
import numpy as np
face_img = np.array(face_img)

PIL

1
2
3
4
from PIL import Image
# 将数组对象读取成图片
face_img = Image.fromarray(face_img)
#

增加图片多样性(或者叫图片增广)

上采样

最近邻插值

1
face_img = face_img.resize((aug_size, aug_size), Image.NEAREST)

双线性插值

1
face_img = face_img.resize((aug_size, aug_size), Image.BILINEAR)

图片压缩

1
2
3
4
5
6
7
# 随意生成压缩尺寸 使用 OpenCV 中的 cv2.IMWRITE_JPEG_QUALITY 常量来指定 JPEG 图像的编码质量参数,并将其与具体的质量值 quality 组成一个列表 encode_param。
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), quality]
# 使用 OpenCV 的 cv2.imencode 函数将图像编码为指定格式(这里是 PNG 格式)。函数的参数包括要保存的图像格式、图像数据和编码参数。结果是一个包含两个元素的元组,其中第一个元素表示是否成功编码,第二个元素是包含编码后的二进制数据的 NumPy 数组。
face_img_encode = cv2.imencode('.png', face_img, encode_param)[1]
# 使用 OpenCV 的 cv2.imdecode 函数,该函数接受两个参数:编码后的二进制数据(face_img_encode)和读取图像的标志(这里是 cv2.IMREAD_COLOR,表示以彩色图像的方式读取)。
face_img = cv2.imdecode(face_img_encode, cv2.IMREAD_COLOR)

图片裁剪:就是切片

1
2
face_img = face_img[60:317, 30:287, :]
mask = mask[60:317, 30:287, :]

图像反转

水平翻转

1
2
face_img = np.flip(face_img, 1)
mask = np.flip(mask, 1)

垂直翻转

1
2
face_img = np.flip(face_img, 0)
mask = np.flip(mask, 0)

定义计算人脸图片的68个数据点(也可以封装成一个类)

核心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 读入图片,一般用cv2的库
import cv2
import dlib
img = cv2.imread(img_path)
# 加载模型文件
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
# 定义人脸检测器
detector = dlib.get_frontal_face_detector()
# 检测人脸, 其中1可以理解位定义的计算开销,用faces是因为可能检测到不止一个脸
faces = detector(img, 1)
# 遍历每一个检测到的人脸
for face in faces:
# 获得68个人脸数据点
landmarks = predictor(img, face)

如果想将读取的68个人脸数据点保存成如下格式

1
2
3
{"000_0000.png": [[56, 143], [57, 168], [61, 192], [67, 216], [76, 238], [93, 257], [112, 273], [133, 288], [156, 291], [178, 287], [198, 271], [219, 256], [236, 237], [246, 216], [250, 192], [252, 167], [252, 142], [69, 131], [84, 123], [102, 123], [119, 126], [137, 132], [178, 130], [195, 122], [213, 119], [230, 119], [244, 126], [158, 149], [158, 168], [158, 186], [159, 205], [140, 211], [148, 214], [158, 219], [168, 214], [176, 210], [91, 150], [102, 143], [116, 144], [127, 154], [115, 156], [101, 156], [188, 152], [199, 142], [213, 141], [224, 148], [214, 153], [201, 154], [117, 232], [134, 229], [148, 228], [158, 231], [168, 228], [181, 229], [195, 232], [182, 246], [169, 253], [158, 254], [147, 254], [132, 247], [125, 234], [147, 238], [158, 239], [168, 237], [188, 234], [168, 237], [158, 239], [147, 238]], "001_0000.png": [[56, 143], [57, 168], [61, 192], [67, 216], [76, 238], [93, 257], [112, 273], [133, 288], [156, 291], [178, 287], [198, 271], [219, 256], [236, 237], [246, 216], [250, 192], [252, 167], [252, 142], [69, 131], [84, 123], [102, 123], [119, 126], [137, 132], [178, 130], [195, 122], [213, 119], [230, 119], [244, 126], [158, 149], [158, 168], [158, 186], [159, 205], [140, 211], [148, 214], [158, 219], [168, 214], [176, 210], [91, 150], [102, 143], [116, 144], [127, 154], [115, 156], [101, 156], [188, 152], [199, 142], [213, 141], [224, 148], [214, 153], [201, 154], [117, 232], [134, 229], [148, 228], [158, 231], [168, 228], [181, 229], [195, 232], [182, 246], [169, 253], [158, 254], [147, 254], [132, 247], [125, 234], [147, 238], [158, 239], [168, 237], [188, 234], [168, 237], [158, 239], [147, 238]]}
结构
(字典){"图片路径":[[x, y](每个点坐标),[]](68个数据点), "":[[],[],……], …… }
1
2
3
4
5
6
landmarks_dic = {}
for _, face in enumerate(faces):
landmarks = predictor(img, face)
# 提前声明二维列表
landmarks_list = [[i.x, i.y] for i in landmarks.parts()]
landmark_dic["pic_path"] = landmarks_list

定义生成BI数据集的类

初始化

读取json文件到内存字典中

json文件中key是图片路径,value是68个数据点

如何计算:

定义只包含图片路径的列表

定义生成遮罩时的仿射变换

生成一个“数据点”,包括backgroundface,foregroundface,类型(fake or real), mask

从图片路径列表中随机选择一个图片的路径

按50%随机生成图片类型(fake or real)

如果是fake,生成混合后的图片并利用公式2将得到的mask转换成FaceXray

根据给定的backgroundface路径读取图片对象和对应的68个数据点

在数据集中搜索相似的脸作为前脸

先随机选取1000个图片路径,并且剔除和backgroundface来自同一个视频(数据集里保存在同一个文件夹下的)

通过计算两张图片的68个数据点的欧氏距离来判断相似度,找到欧式距离最小的那个作为frontface

根据backgroundface和其landmark生成4种mask

将mask进行仿射变换,随机腐蚀和膨胀来增强mask的多样性可以参考这篇文章

混合前对foregroundface进行颜色变换以适应backgroundface

将mask,foregroundface和backgroundface混合

如果是real,直接读取图片,并且生成mask的灰度图

注:所有图片的大小都是317 * 717

最终生成bi数据集的结构

影响:MyGetBIDataset 271行save_bi_dataset

bi_dataset

1
2
3
4
5
6
7
8
| —— true
| | —— face
| | | —— 1.png
| | | —— 2.png
| | —— mask
| | | —— 1.png
| | | —— 2.png
| | —— fake

获得当时数据集最后格式的方法:切片+解包

比如对于your/file/000/background_face.jpg,要获取000/background_face.jpg,用如下写法

1
2
3
4
5
import os
background_face_path = "your/file/000/background_face.jpg"
# 拆分路径为目录和文件名
final_two = os.path.join(*list(background_face_path.split('/'))[-2::])
print(final_two)

命名的时候用是第几张图片命名

如何优雅地用一行代码计算一个文件夹里所有文件的数量

利用列表推导式和匿名函数实现

1
count_files = lambda folder_path : len([f for f in os.listdir(foler_path) if os.path.isfile(os.path.join(folder_path, f))])

computer_landmarksbug修复

用try-except

直接往文件里面写

处理错误

定义保存错误信息的文件位置

配置错误函数

将错误写入错误函数指向的文件

1
2
3
4
5
6
7
8
9
try:
except Exception as e:
logging.basicConfig(filename="ErrorLogs", level=logging.ERROR)
# logging.basicConfig: 这是一个配置基础日志系统的函数,用于配置日志记录的基本设置。在这里,它指定了将日志写入文件,而不是默认的控制台输出
# filename=GETBIERRORLOG: 这部分指定了日志文件的路径和名称。 GETBIERRORLOG 是一个变量,它应该在这一行代码之前定义,并包含了日志文件的完整路径。
# level=logging.ERROR: 这设置了日志记录的级别。在这里,设置为ERROR,表示只记录错误级别的日志消息,包括错误和更严重的消息。这有助于筛选并专注于捕捉代码中的潜在问题。
logging.error(f"{str(e)}")
# 将错误信息写入上面配置的错误函数指向的文件

定义数据集dataset(封装成一个类)

需要写入h5文件

h5文件里面要包含些什么?

HRNet网络的使用

HRNet网络介绍

从官方下载HRNet的网络

其中加载预训练模型的位置

default中

1
common params for NETWORK

default作用说明里有这句

1
定义模型配置,_C.MODEL:

在default搜索_C.MODEL,发现

1
_C.MODEL.PRETRAINED = ''

修改为之前下载的预训练函数位置

default.py中输出种类

1
_C.MODEL.NUM_CLASSES = 2

default.py中修改传入的图片大小

1
_C.MODEL.IMAGE_SIZE

修改传入的图片种类

1
_C.DATASET.DATA_FORMAT = 'png'

default中

1
DATASET related params

传入的图片种类

1
_C.DATASET.DATA_FORMAT = 'png'

数据集根路径

1
_C.DATASET.ROOT = ''

default中

1
# train

迭代轮数

1
_C.TRAIN.END_EPOCH = 150000

batch_size设置为32

1
_C.TRAIN.LR = 0.002

总迭代轮数设置为200,000

1
_C.TRAIN.END_EPOCH = 200000

学习率一开始设置成0.002

1
_C.TRAIN.LR = 0.002

使用Adam优化器

1
_C.TRAIN.OPTIMIZER = 'adam'

在最后的 50,000 次迭代中线性衰减为 0。

1

在tools/train.py中找到data_loader

lib/core/function.py中定义了训练函数

我还是有点懵,这到底怎么传文件啊

我现在就是按它哪个要求修改了分类的参数,但还是迷糊