如果你还没有理清预测部分的代码,那请耐心的看以下的代码注释,一定能够帮助您全部理清顺序
包含以下相关文件
meta
all_data_list :列举的是数据集中所包含的所有训练数据,以npy格式结尾;
anno_path : 是对应文件的相对存放路径;
area6_data_label :为验证训练模型时所用到数据集的数据文件列表;
class_name :数据集所包含的所有13类分类类别;
batch_inference.py :用于预测分类,将预测结果导出
collect_indoor3d_data :用于生成npy格式文件
indoor3d_uti.py :包含了作者写的功能性函数
batch_inference.py
以下只贴出比较关键的代码解释
BATCH_SIZE = FLAGS.batch_size
NUM_POINT = FLAGS.num_point
MODEL_PATH = FLAGS.model_path
GPU_INDEX = FLAGS.gpu
DUMP_DIR = FLAGS.dump_dir
if not os.path.exists(DUMP_DIR): os.mkdir(DUMP_DIR)
LOG_FOUT = open(os.path.join(DUMP_DIR, 'log_evaluate.txt'), 'w')
LOG_FOUT.write(str(FLAGS)+'\n')
DIR = os.path.join(ROOT_DIR,'data/npy')
"""我自己创建的文件夹,提供collect_indoor3d_data.py生成的npy文件存储路径"""
ROOM_PATH_LIST = [os.path.join(DIR,line.rstrip()) for line in open(FLAGS.room_data_filelist)]
"""room_data_filelist为数据集npy按顺序排列,将其文件路径一一列出"""
NUM_CLASSES = 13
"""13类分类"""
定义全局变量,在sem_seg中创建dump文件夹,其中创建log日志文件,将所有全局变量写入日志文件内。
def evaluate():
is_training = False
with tf.device('/gpu:'+str(GPU_INDEX)):
pointclouds_pl, labels_pl = placeholder_inputs(BATCH_SIZE, NUM_POINT)
is_training_pl = tf.placeholder(tf.bool, shape=())
"""定义输入输出占位符 """
# simple model
pred = get_model(pointclouds_pl, is_training_pl)
""" 模型训练 """
loss = get_loss(pred, labels_pl)
""" 计算损失"""
pred_softmax = tf.nn.softmax(pred)
""" 将预测结果归一化"""
#tf.nn.softmax()就是把一个N*1的向量归一化为(0,1)之间的值
# Add ops to save and restore all the variables.
saver = tf.train.Saver()
""" 设置保存模型"""
构建整个训练网络流程的图,很普通的操作
# Create a session
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
config.allow_soft_placement = True
config.log_device_placement = True
sess = tf.Session(config=config)
# Restore variables from disk.
saver.restore(sess, MODEL_PATH)
log_string("Model restored.")
创建会话,恢复模型,为预测做准备
ops = {'pointclouds_pl': pointclouds_pl,
'labels_pl': labels_pl,
'is_training_pl': is_training_pl,
'pred': pred,
'pred_softmax': pred_softmax,
'loss': loss}
total_correct = 0
""" 总正确数"""
total_seen = 0
""" 总个数 """
构建需要用到的输入的字典
for room_path in ROOM_PATH_LIST:
"""将272个房间npy文件路径一一进行预测"""
out_data_label_filename = os.path.basename(room_path)[:-4] + '_pred.txt'
"""生成存储预测对应房间名称的点云数据的空文本"""
out_data_label_filename = os.path.join(DUMP_DIR, out_data_label_filename)
""" 添加存放路径"""
out_gt_label_filename = os.path.basename(room_path)[:-4] + '_gt.txt'
"""生成存储实际对应房间名称的点云数据的空文本"""
out_gt_label_filename = os.path.join(DUMP_DIR, out_gt_label_filename)
"""添加存储路径"""
print(room_path, out_data_label_filename)
a, b = eval_one_epoch(sess, ops, room_path, out_data_label_filename, out_gt_label_filename)
"""单次预测"""
total_correct += a
total_seen += b
fout_out_filelist.write(out_data_label_filename+'\n')
fout_out_filelist.close()
log_string('all room eval accuracy: %f'% (total_correct / float(total_seen)))
将每个房间的文件依次进行预测
def eval_one_epoch(sess, ops, room_path, out_data_label_filename, out_gt_label_filename):
error_cnt = 0
is_training = False
total_correct = 0
total_seen = 0
loss_sum = 0
total_seen_class = [0 for _ in range(NUM_CLASSES)]
""" 创建13个元素的列表,用于存放总的每个类的个数 """
total_correct_class = [0 for _ in range(NUM_CLASSES)]
""" 创建13个元素的列表,存放总的每个类的正确个数"""
if FLAGS.visu:
fout = open(os.path.join(DUMP_DIR, os.path.basename(room_path)[:-4]+'_pred.obj'), 'w')
fout_gt = open(os.path.join(DUMP_DIR, os.path.basename(room_path)[:-4]+'_gt.obj'), 'w')
fout_data_label = open(out_data_label_filename, 'w')
""" _pred.txt用于写入预测点云数据 """
fout_gt_label = open(out_gt_label_filename, 'w')
""" _gt.txt用于写入实际点云数据 """
current_data, current_label = indoor3d_util.room2blocks_wrapper_normalized(room_path, NUM_POINT)
"""
将点云数据分块化
current_data包含的是所有块中的点云信息
current_label包含的是所有块中的点云的标签"""
current_data = current_data[:,0:NUM_POINT,:]
current_label = np.squeeze(current_label)
# Get room dimension..获取房间尺寸
data_label = np.load(room_path)
data = data_label[:,0:6]
max_room_x = max(data[:,0])
max_room_y = max(data[:,1])
max_room_z = max(data[:,2])
file_size = current_data.shape[0]
num_batches = file_size // BATCH_SIZE
print(file_size)
以上都是对数据进行预处理,我们进入以下函数进行详细分析 current_data, current_label是如何生成的
current_data, current_label =indoor3d_util.room2blocks_wrapper_normalized(room_path, NUM_POINT)
room2blocks_wrapper_normalized() :提取出npy点云信息
def room2blocks_wrapper_normalized(data_label_filename, num_point, block_size=1.0, stride=1.0,
random_sample=False, sample_num=None, sample_aug=1):
if data_label_filename[-3:] == 'txt':
data_label = np.loadtxt(data_label_filename)
elif data_label_filename[-3:] == 'npy':
data_label = np.load(data_label_filename)
""" 将房间里的所有点信息读取出,包含xyzRGBL,命为data_label"""
else:
print('Unknown file type! exiting.')
exit()
return room2blocks_plus_normalized(data_label, num_point, block_size, stride,
random_sample, sample_num, sample_aug)
将单个房间的所有点云信息读取出来后,进入room2block_plius_normalized()数据处理
room2blocks_plus_normalized():
def room2blocks_plus_normalized(data_label, num_point, block_size, stride,
random_sample, sample_num, sample_aug):
"""
具有输入文件名和RGB预处理。 对于每个块集中XYZ,将归一化XYZ添加为678通道
"""
data = data_label[:,0:6]
"""
将所有点的XYZRGB读出,data为列表
"""
data[:,3:6] /= 255.0
"""
将RGB进行归一化
"""
label = data_label[:,-1].astype(np.uint8)
"""
提取出所有label
"""
max_room_x = max(data[:,0])
max_room_y = max(data[:,1])
max_room_z = max(data[:,2])
"""
将最大的xyz取出,进行xyz归一化做准备
"""
data_batch, label_batch = room2blocks(data, label, num_point, block_size, stride,
random_sample, sample_num, sample_aug)
"""
data_batch包含了所有块的点云信息,包含xyzrgb
label_batch包含了所有块的点云标签
"""
new_data_batch = np.zeros((data_batch.shape[0], num_point, 9))
"""
创建个(30,4096,9),将所有信息都填入新的数组中
"""
for b in range(data_batch.shape[0]):
"""
一共有30个块,每个块进行读取
"""
new_data_batch[b, :, 6] = data_batch[b, :, 0]/max_room_x
new_data_batch[b, :, 7] = data_batch[b, :, 1]/max_room_y
new_data_batch[b, :, 8] = data_batch[b, :, 2]/max_room_z
"""
将房间的归一化坐标填入678
"""
minx = min(data_batch[b, :, 0])
"""
最小x坐标
"""
miny = min(data_batch[b, :, 1])
"""
最小y坐标
"""
data_batch[b, :, 0] -= (minx+block_size/2)
data_batch[b, :, 1] -= (miny+block_size/2)
new_data_batch[:, :, 0:6] = data_batch
return new_data_batch, label_batch
data包含单个房间的所有xyzRGB信息,并将RGB归一化,利用room2blocks将点云数据进行分块化,生成data_batch(包含30个块的4096个点云xyzRGB信息), label_batch(对应的每个快的标签),创建new_data_batch(30,4096,9),将xyz坐标除最大xyz坐标作为归一化坐标填入new_data_batch的678。
room2blocks():将data进行分块化成1m x 1m的block
def room2blocks(data, label, num_point, block_size=1.0, stride=1.0,
random_sample=False, sample_num=None, sample_aug=1):
assert(stride<=block_size)
#np.amax()
limit = np.amax(data, 0)[0:3]
"""
取出xyz的最大坐标,为确定一共有多少block
"""
# Get the corner location for our sampling blocks 获取采样块的拐角位置
xbeg_list = []
ybeg_list = []
if not random_sample:
num_block_x = int(np.ceil((limit[0] - block_size) / stride)) + 1
"""
np.ceil计算大于等于改值的最小整数
x方向的分块数为6
"""
num_block_y = int(np.ceil((limit[1] - block_size) / stride)) + 1
"""
y方向的分块数为5
"""
for i in range(num_block_x):
"""先定x坐标,按y方向逐个移动分块"""
for j in range(num_block_y):
xbeg_list.append(i*stride)
ybeg_list.append(j*stride)
"""
将块索引填入列表中
"""
用画图来解释为如下图
首先确定x坐标为0,然后以y方向逐块分块,当分好后,x坐标加1,再按y方向分块,直至将所有房间全部分块好。接下来继续看代码。
else:
num_block_x = int(np.ceil(limit[0] / block_size))
num_block_y = int(np.ceil(limit[1] / block_size))
if sample_num is None:
sample_num = num_block_x * num_block_y * sample_aug
for _ in range(sample_num):
xbeg = np.random.uniform(-block_size, limit[0])
ybeg = np.random.uniform(-block_size, limit[1])
xbeg_list.append(xbeg)
ybeg_list.append(ybeg)
# Collect blocks
block_data_list = []
block_label_list = []
idx = 0
for idx in range(len(xbeg_list)):
xbeg = xbeg_list[idx]
ybeg = ybeg_list[idx]
"""
取出分好的块
"""
xcond = (data[:,0]<=xbeg+block_size) & (data[:,0]>=xbeg)
"""
开始获取块中点云信息,做个判断,满足x坐标<1且同时x坐标>1为true,否则false
"""
ycond = (data[:,1]<=ybeg+block_size) & (data[:,1]>=ybeg)
"""
满足y坐标是否在此块中
"""
cond = xcond & ycond
"""
xy坐标同时满足的为true,相当于确定了点的索引
"""
if np.sum(cond) < 100: # discard block if there are less than 100 pts.
continue
"""
如果少于100个点,则丢弃这个块
"""
block_data = data[cond, :]
"""
data包含所有点的xyzRGB信息,提取出符合该块范围的点云
"""
block_label = label[cond]
"""
提取出对应的标签
"""
# randomly subsample data #随机子样本数据
block_data_sampled, block_label_sampled = \
sample_data_label(block_data, block_label, num_point)
"""
将每个块的点都采样为4096个点,
块中多于4096的点进行随机选择4096个,块中少于4096进行重复点填充至4096
"""
block_data_list.append(np.expand_dims(block_data_sampled, 0))
block_label_list.append(np.expand_dims(block_label_sampled, 0))
return np.concatenate(block_data_list, 0), \
np.concatenate(block_label_list, 0)
"""
block_data_list包含了所有块的点云信息,包含xyzrgb
block_label_list包含了所有块的点云标签
"""
以上是indoor3d_util中的代码,紧接着回到eval_one_epoch数据预处理结束之后
for batch_idx in range(num_batches):
start_idx = batch_idx * BATCH_SIZE
end_idx = (batch_idx+1) * BATCH_SIZE
cur_batch_size = end_idx - start_idx
feed_dict = {ops['pointclouds_pl']: current_data[start_idx:end_idx, :, :],
ops['labels_pl']: current_label[start_idx:end_idx],
ops['is_training_pl']: is_training}
loss_val, pred_val = sess.run([ops['loss'], ops['pred_softmax']],
feed_dict=feed_dict)
if FLAGS.no_clutter:
pred_label = np.argmax(pred_val[:,:,0:12], 2) # BxN
else:
pred_label = np.argmax(pred_val, 2) # BxN
分批次进行网络预测
for b in range(BATCH_SIZE):
"""分批次对每个预测的数据进行处理,这里BATCH_SIZE设置为1,每次只处理一个数据"""
pts = current_data[start_idx+b, :, :]
"""取出当前批次的点云数据,包括xyz坐标"""
l = current_label[start_idx+b,:]
pts[:,6] *= max_room_x
pts[:,7] *= max_room_y
pts[:,8] *= max_room_z
"""678加上房间的标准化位置坐标"""
pts[:,3:6] *= 255.0
"""颜色255占位"""
pred = pred_label[b, :]
for i in range(NUM_POINT):
color = indoor3d_util.g_label2color[pred[i]]
""" 添加颜色"""
color_gt = indoor3d_util.g_label2color[current_label[start_idx+b, i]]
if FLAGS.visu:
fout.write('v %f %f %f %d %d %d\n' % (pts[i,6], pts[i,7], pts[i,8], color[0], color[1], color[2]))
""" 将点云信息和颜色信息写入预测的文件"""
fout_gt.write('v %f %f %f %d %d %d\n' % (pts[i,6], pts[i,7], pts[i,8], color_gt[0], color_gt[1], color_gt[2]))
""" 写入实际点云文件"""
fout_data_label.write('%f %f %f %d %d %d %f %d\n' % (pts[i,6], pts[i,7], pts[i,8], pts[i,3], pts[i,4], pts[i,5], pred_val[b,i,pred[i]], pred[i]))
fout_gt_label.write('%d\n' % (l[i]))
correct = np.sum(pred_label == current_label[start_idx:end_idx,:])
total_correct += correct
total_seen += (cur_batch_size*NUM_POINT)
loss_sum += (loss_val*BATCH_SIZE)
for i in range(start_idx, end_idx):
for j in range(NUM_POINT):
l = current_label[i, j]
total_seen_class[l] += 1
total_correct_class[l] += (pred_label[i-start_idx, j] == l)
log_string('eval mean loss: %f' % (loss_sum / float(total_seen/NUM_POINT)))
log_string('eval accuracy: %f'% (total_correct / float(total_seen)))
fout_data_label.close()
fout_gt_label.close()
if FLAGS.visu:
fout.close()
fout_gt.close()
return total_correct, total_seen