SemanticKITTI:Development Kit的学习记录-可视化源码阅读

目的:通过阅读源码了解标签映射方法

重点应该是第八部分和第九部分

主函数文件visualize.py

/====================/
代码第一部分
if name == ‘main’: (含义:如果我直接用./visualize.py 运行visualize.py 那么运行该部分内的代码,如果visualize.py 被另一个xxx.py 文件导入,我运行xxx.py文件 那么就不运行该部分代码)

parser = argparse.ArgumentParser(“./visualize.py”) (这个函数是命令行解析函数,通过该函数可以在终端向程序输入参数,或者从配置文件中读取参数)
比如 visualize.py 的运行命令 ./visualize.py --sequence 11 --dataset /media/pf/Elements/DATA/Snowy_Data/Open_dataset/WADS/11_20211109T212945Z_001/
–sequence 代表要读的序列号 该序列号文件下 存放 两个文件 labels velodyne
–dataset 代表序列号文件所在的路径 如 在/media/pf/Elements/DATA/Snowy_Data/Open_dataset/WADS/11_20211109T212945Z_001/ 这个路径下有一个 文件名为11(序列号)的文件,这个文件下放着标签文件夹和点云bin文件夹

就可视化来说 只需用到该部分中的–sequence --dataset

该部分最后
FLAGS, unparsed = parser.parse_known_args()(这个函数就算将传入的参数保存起来并传给FLAGS,没用到的参数就传给unparsed)

/========================/

第二部分是参数的打印
有效的参数就两个
print(“Dataset”, FLAGS.dataset)
print(“Sequence”, FLAGS.sequence)

/====================/
第三部分 读取配置文件
CFG = yaml.safe_load(open(FLAGS.config, ‘r’)) //semantic-kitti.yaml
疑问:配置文件中的content 是如何获得的。
降雪点 对应 标签 0 : “unlabeled” 0 : [0, 0, 0]黑色

/====================/
第四部分 固定序列名,判断序列文件下的点云文件夹是否存在
FLAGS.sequence = ‘{0:02d}’.format(int(FLAGS.sequence)) (‘{:02d}’.format(i)表示将i变为两位十进制数字的字符串,不够两位用0填充。)

scan_paths = os.path.join(FLAGS.dataset, “sequences”,
FLAGS.sequence, “velodyne”) (含义:/media/pf/Elements/DATA/Snowy_Data/Open_dataset/WADS/11_20211109T212945Z_001/ sequences/11/velodyne)
if os.path.isdir(scan_paths):

/========================/
第五部分 将点云所有bin文件名 填充到一个列表中
os.walk通过在目录树中游走输出在目录中的文件名,向上或者向下。
scan_names.sort()将文件名按顺序排列(由小到大)

//
第六部分获取对应的标签labels文件名
if not FLAGS.ignore_semantics: (ignore_semantics 默认为 false if not 返回true)
if FLAGS.predictions is not None:(FLAGS.predictions 默认为 FALSE 条件返回 false ,因此获取手工标签所在路径;如果FLAGS.predictions是 true 就读取预测后获得标签的路径)
label_paths = os.path.join(FLAGS.dataset, “sequences”,
FLAGS.sequence, “labels”)
判断标签路径是否存在
label_names = [os.path.join(dp, f) for dp, dn, fn in os.walk(
os.path.expanduser(label_paths)) for f in fn]
label_names.sort() (读取和数据集文件并列的手动标注的标签,并排序)
/
/
第七部分 检查标签文件数与bin文件数是否一致
if not FLAGS.ignore_safety:
assert(len(label_names) == len(scan_names))

//
第八部分 判断是否需要语义 不需要则直接投影 // 需要则加载配置文件中的BGR配置 再投影
if FLAGS.ignore_semantics:
scan = LaserScan(project=True) # project all opened scans to spheric proj
else:
color_dict = CFG[“color_map”]
nclasses = len(color_dict)
scan = SemLaserScan(nclasses, color_dict, project=True)
重点要学习SemLaserScan这个函数,它实现了投影功能,scan 是SemLaserScan实例化后的一个对象
/
/
第九部分 创建可视化
需要语义 semantics = true
if not semantics:
label_names = None
vis = LaserScanVis(scan=scan,
scan_names=scan_names,
label_names=label_names,
offset=FLAGS.offset,
semantics=semantics, instances=instances and semantics)
LaserScanVis 如何将点云进行可视化
/========================/
第十部分 设置按键功能 运行可视化

print instructions

print(“To navigate:”)
print(“\tb: back (previous scan)”)
print(“\tn: next (next scan)”)
print(“\tq: quit (exit program)”)

run the visualizer

vis.run()
/

按上述重点,学习laserscan.py 中的class SemLaserScan(LaserScan):

**class SemLaserScan(LaserScan)😗*类SemLaserScan是继承LaserScan的,也就是这个类既有点云数据,也有语义标签,对应颜色
EXTENSIONS_LABEL = [‘.label’] 文件属性 label
/=========================/
第一部分 初始化
def init(self, nclasses, sem_color_dict=None, project=False, H=64, W=1024, fov_up=3.0, fov_down=-25.0):
super(SemLaserScan, self).init(project, H, W, fov_up, fov_down)
self.reset()
self.nclasses = nclasses # number of classes
第一个init 是自己的初始化 也就是获取一些参数 比如标签类的数量,语义色彩图下的元素 投影的高宽 垂直角范围
第二个init 是调用父类的初始化 即 将投影二维图先用0和-1进行填充等待点云数据

创建语义色彩
max_sem_key = 0
for key, data in sem_color_dict.items():
if key + 1 > max_sem_key:
max_sem_key = key + 1
self.sem_color_lut = np.zeros((max_sem_key + 100, 3), dtype=np.float32)
for key, value in sem_color_dict.items():
self.sem_color_lut[key] = np.array(value, np.float32) / 255.0
第一个for 是为了获得语义色彩种类的最大值
第二个for是为了将BGR放入sem_color_lut对应的key位置中 并进行归一化操作 / 255

创建实例色彩
# make instance colors
max_inst_id = 100000
self.inst_color_lut = np.random.uniform(low=0.0,
high=1.0,
size=(max_inst_id, 3))
# force zero to a gray-ish color
self.inst_color_lut[0] = np.full((3), 0.1) 强制0为灰色
从0到1的区间随机生成 max_inst_id各实例色彩 每个实例色彩包含3个值 [xxx, xxx, xxx]
/====================/
第二部分 重置
对应第一部分的 self.reset()
调用父类中的重置函数 将投影图 填充为0和-1的数 包含(每个点的坐标、强度,投影图每个位置点的深度、xyz坐标、强度、点云中索引、投影图位置、掩码,投影前的深度)
除上述外,还有自己特有属性的重置 用0代替
语义标签,语义色彩
实例标签,实例色彩
投影后的语义标签
投影后的语义色彩
投影后的实例标签
投影后的实例色彩

//
第三部分 打开标签文件(打开原始扫描并填充属性)
def open_label(self, filename):
检测文件名是否是字符串
if not isinstance(filename, str):
检查文件名后缀是否为labels
if not any(filename.endswith(ext) for ext in self.EXTENSIONS_LABEL):
没有问题则打开标签文件
label = np.fromfile(filename, dtype=np.uint32) 用文本或二进制文件中的数据构造一个数组
label = label.reshape((-1))
设置这些标签值
self.set_label(label)
/
/
第四部分 用来自np中的标签值(上述中label = np.fromfile)设置点云
def set_label(self, label):
如果标签数与点云点数一致,则进行属性填充
if label.shape[0] == self.points.shape[0]:
self.sem_label = label & 0xFFFF # semantic label in lower half label下半部分为语义值
self.inst_label = label >> 16 # instance id in upper half // label 上半部分为实例值

进行完整性检查

assert((self.sem_label + (self.inst_label << 16) == label).all())
进行标签投影
self.do_label_projection()

/========================/
第五部分 用每个语义标签的颜色给点云着色
def colorize(self):
self.sem_label_color = self.sem_color_lut[self.sem_label]
self.sem_label_color = self.sem_label_color.reshape((-1, 3))
self.inst_label_color = self.inst_color_lut[self.inst_label]
self.inst_label_color = self.inst_label_color.reshape((-1, 3))

//
第六部分 只将颜色映射到存在的标签
def do_label_projection(self):
mask = self.proj_idx >= 0 投影的索引值存在
语义标签值以及对应的颜色
self.proj_sem_label[mask] = self.sem_label[self.proj_idx[mask]]
self.proj_sem_color[mask] = self.sem_color_lut[self.sem_label[self.proj_idx[mask]]]
实例标签值以及对应的颜色
self.proj_inst_label[mask] = self.inst_label[self.proj_idx[mask]]
self.proj_inst_color[mask] = self.inst_color_lut[self.inst_label[self.proj_idx[mask]]]
///
上述的所有函数应该是在LaserScanVis中被调用
class LaserScanVis: 类,该类为点云创建和处理可视化工具
/
============/
第一部分 初始化
def init(self, scan, scan_names, label_names, offset=0,
semantics=True, instances=False):
self.scan = scan
self.scan_names = scan_names
self.label_names = label_names
self.offset = offset
self.total = len(self.scan_names)
self.semantics = semantics
self.instances = instances
scan 语义投影类实例对象
scan_names 存放各个点云bin文件 list
label_names 存放各个标签label文件 list
semantics = true
instances = false

self.reset()
self.update_scan()
//重置并更新扫描
/================================/
第二部分 重置
三维
self.action = “no” # no, next, back, quit are the possibilities (应该与读帧导航按键有关,n,b,q这几个按键)
self.canvas = SceneCanvas(keys=‘interactive’, show=True) 创建新画布 interactive:escape和F11将分别关闭画布和切换到全屏模式

self.canvas.events.key_press.connect(self.key_press) //获取按键
self.canvas.events.draw.connect(self.draw)//进行渲染

self.grid = self.canvas.central_widget.add_grid() // 添加网格

self.scan_view = vispy.scene.widgets.ViewBox(
border_color=‘white’, parent=self.canvas.scene) // 设置窗口小部件显示框属性 边框颜色为白色
self.grid.add_widget(self.scan_view, 0, 0)// 将窗口小部件添加到网格里
self.scan_vis = visuals.Markers() // 视觉显示标记符号。实例化一个对象
self.scan_view.camera = ‘turntable’// 观看视角可旋转
self.scan_view.add(self.scan_vis) // 添加可视图
visuals.XYZAxis(parent=self.scan_view.scene) //添加坐标系用于指示坐标系方向的简单3D轴。坐标轴是x=红色,y=绿色,z=蓝色。

如果有语义信息
# add semantics
if self.semantics:
print(“Using semantics in visualizer”)
self.sem_view = vispy.scene.widgets.ViewBox(
border_color=‘white’, parent=self.canvas.scene)
self.grid.add_widget(self.sem_view, 0, 1) //放在第一行第二列网格
self.sem_vis = visuals.Markers()
self.sem_view.camera = ‘turntable’
self.sem_view.add(self.sem_vis)
visuals.XYZAxis(parent=self.sem_view.scene)
# self.sem_view.camera.link(self.scan_view.camera)

if self.instances:
print(“Using instances in visualizer”)
self.inst_view = vispy.scene.widgets.ViewBox(
border_color=‘white’, parent=self.canvas.scene)
self.grid.add_widget(self.inst_view, 0, 2) //放在第一行第三列网格
self.inst_vis = visuals.Markers()
self.inst_view.camera = ‘turntable’
self.inst_view.add(self.inst_vis)
visuals.XYZAxis(parent=self.inst_view.scene)
# self.inst_view.camera.link(self.scan_view.camera)

二维
self.img_canvas = SceneCanvas(keys=‘interactive’, show=True,
size=(self.canvas_W, self.canvas_H * self.multiplier)) //创建新的画布
self.img_grid = self.img_canvas.central_widget.add_grid() //网格对象

interface (n next, b back, q quit, very simple)

self.img_canvas.events.key_press.connect(self.key_press) //按键
self.img_canvas.events.draw.connect(self.draw) // 作画

add a view for the depth 原始点云

self.img_view = vispy.scene.widgets.ViewBox(
border_color=‘white’, parent=self.img_canvas.scene) //生存图形框
self.img_grid.add_widget(self.img_view, 0, 0) //图形位置
self.img_vis = visuals.Image(cmap=‘viridis’) // 获取应用于亮度(单波段)数据的colormap对象。
self.img_view.add(self.img_vis)

add semantics 语义点云

if self.semantics:
self.sem_img_view = vispy.scene.widgets.ViewBox(
border_color=‘white’, parent=self.img_canvas.scene)
self.img_grid.add_widget(self.sem_img_view, 1, 0) //放在第二行
self.sem_img_vis = visuals.Image(cmap=‘viridis’)
self.sem_img_view.add(self.sem_img_vis)

add instances 实例图

if self.instances:
self.inst_img_view = vispy.scene.widgets.ViewBox(
border_color=‘white’, parent=self.img_canvas.scene)
self.img_grid.add_widget(self.inst_img_view, 2, 0)
self.inst_img_vis = visuals.Image(cmap=‘viridis’)
self.inst_img_view.add(self.inst_img_vis)
/========================================/
第三部分 获得色彩图
def get_mpl_colormap(self, cmap_name): 其中 cmap_name=viridis
初始化颜色图
cmap = plt.get_cmap(cmap_name) (所谓 colormap(颜色表),就是将一系列颜色按给定的顺序排列在一起。其用处是,我们可以通过某种映射关系,将一系列数值映射到一张 colormap 上去,使不同大小的数值对应不同的颜色。这样一来,在绘制填色图时便能直观地用颜色来反映数值的分布。)
获取线性颜色 范围
color_range = sm.to_rgba(np.linspace(0, 1, 256), bytes=True)[:, 2::-1]
返回归一化后的范围
return color_range.reshape(256, 3).astype(np.float32) / 255.0

/============================================/
第四部分 更新扫描
def update_scan(self):
注意 self.scan 是之前SemLaserScan(nclasses, color_dict, project=True)实例化后的对象

self.scan.open_scan(self.scan_names[self.offset]) 首先打开点云文件列表scan_names然后设置点云(坐标,强度)接着进行点云投影
self.scan.open_label(self.label_names[self.offset]) 判断是否有语义打开标签列表//设置标签(语义值、实例值)接着进行标签投影(标签对应的颜色放入proj_sem_color
self.scan.colorize() 用每一个语义标签的颜色渲染点云// 代码内容只是将标签值放入字典中找到对应的标签颜色 sem_label_color是一个np数组

then change names

title = "scan " + str(self.offset) 设置标题 并显示当前是第几帧
self.canvas.title = title
self.img_canvas.title = title

plot scan

power = 16 //这个16代表什么含义???

print()

range_data = np.copy(self.scan.unproj_range) //获取未投影前的深度值

print(range_data.max(), range_data.min())

range_data = range_data**(1 / power) //将深度值除以16

print(range_data.max(), range_data.min())

viridis_range = ((range_data - range_data.min()) /
(range_data.max() - range_data.min()) *
255).astype(np.uint8) //将指标正向化 并乘以255 获得对应的颜色值
viridis_map = self.get_mpl_colormap(“viridis”) // 获取颜色图色段
viridis_colors = viridis_map[viridis_range] //根据颜色值获得对应的颜色
self.scan_vis.set_data(self.scan.points, 显示每个符号的位置数组。就是说要渲染的点的位置在哪
face_color=viridis_colors[…, ::-1], 用于绘制内部每个符号的颜色。
edge_color=viridis_colors[…, ::-1], 用于绘制每个符号轮廓的颜色。
size=1) //设置用于显示此可视效果的数据。

plot semantics //对于右侧的语义图 则用语义色彩进行渲染
if self.semantics:
self.sem_vis.set_data(self.scan.points,
face_color=self.scan.sem_label_color[…, ::-1], 原色彩图的三个值为BGR 翻转读取 RGB
edge_color=self.scan.sem_label_color[…, ::-1],
size=1)

if self.instances: 对实例进行渲染
self.inst_vis.set_data(self.scan.points,
face_color=self.scan.inst_label_color[…, ::-1],
edge_color=self.scan.inst_label_color[…, ::-1],
size=1)

渲染二维平面图
# now do all the range image stuff
# plot range image
data = np.copy(self.scan.proj_range) //原始点云投影图像
# print(data[data > 0].max(), data[data > 0].min())
data[data > 0] = data[data > 0]**(1 / power)
data[data < 0] = data[data > 0].min()
# print(data.max(), data.min())
data = (data - data[data > 0].min()) /
(data.max() - data[data > 0].min())
# print(data.max(), data.min())
self.img_vis.set_data(data)
self.img_vis.update()

if self.semantics:
self.sem_img_vis.set_data(self.scan.proj_sem_color[…, ::-1]) //语义点云投影图像
self.sem_img_vis.update()

if self.instances:
self.inst_img_vis.set_data(self.scan.proj_inst_color[…, ::-1]) //实例投影图
self.inst_img_vis.update()

/============================================/
第五部分 交互按键
def key_press(self, event):
self.canvas.events.key_press.block()
self.img_canvas.events.key_press.block()
if event.key == ‘N’:
self.offset += 1
if self.offset >= self.total:
self.offset = 0
self.update_scan()
elif event.key == ‘B’:
self.offset -= 1
if self.offset < 0:
self.offset = self.total - 1
self.update_scan()
elif event.key == ‘Q’ or event.key == ‘Escape’:
self.destroy()

/========================================/
第七部分 是否按压按键
def draw(self, event):
if self.canvas.events.key_press.blocked():
self.canvas.events.key_press.unblock()
if self.img_canvas.events.key_press.blocked():
self.img_canvas.events.key_press.unblock()

/============================================/
第八部分 退出绘画
def destroy(self):
# destroy the visualization
self.canvas.close()
self.img_canvas.close()
vispy.app.quit()

/============================================/
第九部分 运行绘画应用 实现上面的配置
def run(self):
vispy.app.run()

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值