论文地址
Guthub项目地址
将项目clone下来之后,还需要clone另一个项目neural_renderer,这是原项目地址,原项目在高版本的pytorch需要做一些修改,而作者也提供了对其进行修改后的项目地址。
由于neural_renderer环境配置比较麻烦,这部分主要说明我在服务器上具体环境下的配置方法。
环境:Ubuntu 18.04,gcc 7.5.0,NVIDIA GeForce RTX 3090,cuda 11.3
上述环境中cuda版本最重要,可通过nvcc -V
命令查看。如果服务器上有多个cuda版本,可以使用ls /usr/local | grep cuda
查看,并通过修改~/.bashrc
文件指定具体版本。
下面进行conda环境配置
conda create -n fca python=3.6 # 只在python=3.6成功配置过neural_renderer
source activate fca
pip install torch==1.10.2+cu113 torchvision==0.11.3+cu113 -f https://download.pytorch.org/whl/torch_stable.html # 只在该pytorch版本下成功配置过neural_renderer
# 在安装完成pytorch后,必须先安装neural_renderer
cd neural_renderer
python setup.py install
# 如果可以成功安装,问题就基本解决了
# 如果出现类似load_textures.cpython-38-x86_64-linux-gnu.so: undefined symbol: _ZNK3c107SymBool10guard_boolEPKcl的报错,说明前面的步骤没走对
# 安装完成后,可以运行测试程序
pip install imageio skikit-image
python examples/example1.py # 如果顺利运行,可以看到一个进度条
ls -al examples/data
# 此时应该可以看到出现名为example1.gif的文件,且修改时间应该是最新的,如果到达这里,那nueral_renderer的安装完成
cd .. # 返回src目录下
pip install -r requirements.txt # 通过文件自动安装大多数重要的包
# opencv包可能会安装失败,此时应通过conda安装,并且在requirements.txt中注释掉opencv-python>=4.1.2这一行
conda install py-opencv
# 安装完成之后,检查opencv是否可以使用
python -c "import cv2; print(cv2.__version__)" # -c参数会使得后面的字符串作为程序输入,若成功输出结果,说明opencv安装成功。
本文使用前作的数据集,可以直接在这里drive.google.com下载其中文件,并且将其移动到carla_dataset目录下,《Dual Attention Attack》项目也提供了网盘的下载链接。
执行bash ./weights/download_weights.sh
文件,下载yolov3目标检测模型
如果想使用其他的目标检测模型、其他的数据集,可以利用data目录下各种yaml文件并通过其提示使用train.py训练目标检测模型。
在模型进行训练时(train_camouflage_yolov3.py)可能会发现在每轮训练只读取一个文件,位于carla_dataset/train_new/data1.png,我使用的处理方式如下:
import numpy as np
import os
from PIL import Image
def npz2img(input_path, output_path):
# 获取input_path下所有的.npz文件
for file_name in os.listdir(input_path):
if file_name.endswith('.npz'):
print(input_path + file_name)
# 加载.npz文件
npz_data = np.load(os.path.join(input_path, file_name))
# 获取'img'这个KEY值对应的VALUE
image_data = npz_data['img']
# 将numpy数组转为PIL图像
image = Image.fromarray(image_data.astype(np.uint8))
# 创建输出目录,如果不存在
if not os.path.exists(output_path):
os.makedirs(output_path)
# 构建输出文件名(将.npz更改为.jpg)
output_file_name = file_name.replace('.npz', '.png')
output_file_path = os.path.join(output_path, output_file_name)
# 保存图片
image.save(output_file_path)
print(output_file_path)
# 使用示例
input_path = './carla_dataset/train/' # 请替换为您的.npz文件所在路径
output_path = './carla_dataset/train_new/' # 请替换为您希望保存图片的路径
npz2img(input_path, output_path)
def example():
obj_file = './carassets/audi_et_te.obj'
data_path = './carla_dataset/train/'
img_save_dir = './render_test_res/'
vertices, faces = neural_renderer.load_obj(obj_file)
texture_mask = np.zeros((faces.shape[0], 2, 2, 2, 3), 'int8')
with open('./carassets/exterior_face.txt', 'r') as f:
face_ids = f.readlines()
for face_id in face_ids:
texture_mask[int(face_id) - 1, :, :, :, :] = 1
texture_mask = torch.from_numpy(texture_mask).cuda(device=0).unsqueeze(0)
print(texture_mask.size())
faces_var = torch.autograd.Variable(faces.cuda(device=0))
vertices_var = vertices.cuda(device=0)
# Textures
texture_size = 2
textures = np.ones((1, faces.shape[0], texture_size, texture_size, texture_size, 3), 'float32')
textures = torch.from_numpy(textures).cuda(device=0)
print(textures.size())
textures = textures * texture_mask
neural_renderer.save_obj(img_save_dir + 'saveTmp.obj', vertices_var, faces_var, textures[0], texture_size)
TypeError: expected np.ndarray (got Tensor)
: 不需要torch.from_numpy()
操作,画蛇添足,删去即可。assert vertices.ndimension() == 2 AssertionError
: 不需要在vertices或faces后进行[None, :, :]维度扩增操作,删去RuntimeError: CUDA error: an illegal memory access was encountered
: 这个错误非常危险也难以察觉,其实是在调用save_obj函数时,一定要注意删去vertices, faces, textures这三个参数的batch_size维度,这一点非常非常关键,也导致了很多neural_renderer官方issue的“悬案”。这个问题是这样被测试出来的:直接load_obj后用得到的三个参数save_obj,同时观察其所有的shape,发现这三个参数的第一维都不是batch_size而是面的数量。import torch
from torch.utils.data import DataLoader
from data_loader import MyDatasetTestAdv
from tqdm import tqdm
import numpy as np
import sys
import argparse
from PIL import Image
import os
sys.path.append("./neural_renderer/")
import neural_renderer
parser = argparse.ArgumentParser()
parser.add_argument("--batchsize", type=int, default=1)
parser.add_argument("--obj", type=str, default='carassets/audi_et_te.obj')
parser.add_argument("--faces", type=str, default='carassets/exterior_face.txt') # exterior_face all_faces
# parser.add_argument("--textures", type=str, default='textures/texture_camouflage.npy')
parser.add_argument('--textures', type=str, default='texture_in_5_epochs.npy')
parser.add_argument("--datapath", type=str, default="carla_dataset/")
args = parser.parse_args()
BATCH_SIZE = args.batchsize
mask_dir = os.path.join(args.datapath, 'masks/')
obj_file =args.obj
texture_size = 6
vertices, faces, textures = neural_renderer.load_obj(filename_obj=obj_file, texture_size=texture_size, load_texture=True)
# Camouflage Textures
texture_content_adv = torch.from_numpy(np.load(args.textures)).cuda(device=0)
texture_origin =textures[None, :, :, :, :, :].cuda(device=0)
texture_mask = np.zeros((faces.shape[0], texture_size, texture_size, texture_size, 3), 'int8')
with open(args.faces, 'r') as f:
face_ids = f.readlines()
for face_id in face_ids:
if face_id != '\n':
texture_mask[int(face_id) - 1, :, :, :, :] = 1
texture_mask = torch.from_numpy(texture_mask).cuda(device=0).unsqueeze(0)
def cal_texture(texture_content, CONTENT=False):
textures = 0.5 * (torch.nn.Tanh()(texture_content) + 1)
return texture_origin * (1 - texture_mask) + texture_mask * textures
@torch.no_grad()
def run_cam(data_dir, batch_size=BATCH_SIZE):
print(data_dir)
dataset = MyDatasetTestAdv(data_dir, input_size, texture_size, faces, vertices, distence=None, mask_dir=mask_dir, ret_mask=True)
loader = DataLoader(
dataset=dataset,
batch_size=batch_size,
shuffle=False,
# num_workers=2,
)
print(len(dataset))
tqdm_loader = tqdm(loader)
textures_adv = cal_texture(texture_content_adv, CONTENT=True)
dataset.set_textures(textures_adv)
for i, (index, total_img, texture_img, _, filename) in enumerate(tqdm_loader):
texture_img_np = total_img.data.cpu().numpy()[0]
texture_img_np = Image.fromarray(np.transpose(texture_img_np, (1, 2, 0)).astype('uint8'))
filename = filename[0].split('.')[0]
save_path = 'savedImage_test'
if not os.path.exists(save_path):
os.makedirs(save_path)
if not os.path.exists(os.path.join(save_path, filename)):
os.makedirs(os.path.join(save_path, filename))
texture_img_np.save(fr'{save_path}/{filename}/{filename}.png')
save_path = 'savedImage_test'
# Yolo-v5 detection
results = net(texture_img_np)
results.save(fr'{save_path}/{filename}/')
if __name__ == "__main__":
data_dir = f"{args.datapath}test/"
batch_size = 1
input_size = 800
# net = torch.hub.load('ultralytics/yolov5', 'yolov5x') # or yolov5m, yolov5x, custom
net = torch.hub.load('./', 'custom', './yolov5x.pt', source='local')
net.eval()
if torch.cuda.is_available():
net = net.cuda()
run_cam(data_dir)
Attention! 这部分仍存在问题,持续更新中
tensorboard --logdir runs/train
,具体的目录会在程序运行过程中打印到终端,执行代码后即在服务器上默认的6006端口启用tensorboard。然后在本地cmd、shell中执行ssh -L 16006:localhost:6006 -p 33 user@remote_server
,这一段代码首先链接远程服务器33号端口,然后将6006端口转发到本地的16006端口。此后在浏览器中打开http://localhost:16006
就可以打开TensorBoard界面。执行完上面所有步骤后,两个shell可以不关闭,他们只是负责开启tensorboard服务和转发端口的任务。