本文记录了论文《Weakly- and Semi-Supervised Panoptic Segmentation》的代码的阅读笔记。更新于2019.03.08。
文章目录
demo_instanceTrainId_to_dets.m
输入: 读取实例训练标签,CityScapes中有11个stuff类别(stuff_classes = 0:10),8个thing类别(thing_classes = 11:18),读取了object name,忽略值为255的标签(ignore_label = 255)。
输入如下图:
最大值:18001
用于提取的主函数: instanceTrainId_to_dets.m,阅读笔记链接在这里。该函数的输出内容如下图所示(其中tmp是输出博主临时保存的名字),保存到的路径为'data/Cityscapes/gtFine_bboxes/train/panoptic'
。
instanceTrainId_to_dets.m
作用:
根据数据库给定的真值生成bounding boxes,为后续的MCG和GrabCut提供bounding box输入。
输入:
- label:实例训练标签。具体如下图所示。
上层函数对此输入的读取方式:
- is_panoptic:控制是否包含iamge-level stuff class dets,如果包含,设置成True。
- incl_grps:控制是否包含thing groups present in Cityscapes,如果包含,设置成True。
- stuff_classes:stuff类别的训练id(对Cityscapes是0:10)。
- thing_classes:thing类别的训练id(对Cityscapes是11:18)。
- objectNames:加载下来的目标名称。
- ignore_label:需要忽略的标签,对于label中的情况,忽略的是255。
输出:
具体过程:
- 提取出现过的类别并按顺序排列(升序),如下图:
- 忽略其中ignore的标签,变成下图所示:(注意到其中255这个类别没有了)
注:文件中说明,unique_ids中的每一个条目都是一个id
- 如果id>1000,那么这是一个thing类别的实例 = trainId*1000+instance_id ……(case 1)
- 如果id<=1000,那么如果是从0到10,那么就是stuff类别……(case 2),如果is_panoptic则包含image-wide det;如果是从11到18,那么就是thing group……(case 3),如果incl_grps则包含group-wide det。
对于stuff、thing instance和thing group这三个,博主的理解是,stuff是不可数,thing instance是可数目标的单独个体,thing group是同类可数目标的集合。
-
对所有的unique_ids遍历:
若该标签属于case2,那么,xmin和ymin为‘0’,xmax和ymax分别对应整个画布的边缘,class为名字;
若改标签属于case1,那么,找到图片中所有属于这个类别的位置并存入mask,xmin和ymin分别为该mask为1部分的最小列号和行号,xmax和ymax为最大列号和行号,class为名字;
若case3,操作与case1相似,只是最开始的判断条件不同。 -
对所有有效的id,记录类别名称和以0开始的bounding box坐标。输出格式是这样的:
road类别的bndbox:
car类别的bndbox:
demo_merge_cam_mandg.m
输入:
- 多类别分类器中获取的CAMs;
- MCG与GrabCut的融合线索。
注:
代码中没有给出提取CAMs的方法及文件,而是直接给出了CAMs的结果。当然,如果想训练自己的分类器,代码中也提供了下载地址(image-level tags + crops)。
其中论文用的是Grad-CAM,Grabcut和MCG。同样,Grabcut和MCG代码中也没有给出提取方法及文件,只给出了连接:MCG代码链接和OpenCV版的Grabcut。也可以直接下载生成好的融合结果。
输出:
- 首先调用
demo_instanceTrainId_to_dets.m
生成mask,具体看这里。 - 再运行
get_opts
生成必要的辅助内容,如路径、设置等等,具体看这里。 - 最后运行主函数
run_sub
生成最终结果,具体看这里。 - 显示用
visualise_results_cam_mandg
函数,具体看这里。
demo_make_iterative_gt.m
用prediction和prediction scores(可选择是否加上M&G mask)生成下一次迭代需要的真值,这个函数是这一步骤的demo。
这个函数与demo_merge_cam_mandg.m
(点击查看详情)基本相同,主体都是run_sub
(点击查看详情)这个函数。博主注意到的唯一区别就是opt.run_score_thresh
这个参数,demo_merge_cam_mandg.m
这个文件将该参数设成了0,demo_make_iterative_gt.m
这个文件将该参数设成了1。
下图为输出示例:
其他说明
如果要复现论文中的结果,在训练过程中迭代5次之后,将参数opts.run_merge_with_mcg_and_grabcut
设成false,因为此时弱监督模型已经能生成比M&G mask更好的thing类别分割了。
重复训练和生成真值的步骤,直到训练过程的损失不再减小。
get_opts.m
输入: dataset的名字(比如cityscapes),分支(如train、val、train_extra)。
输出: 生成的options。
文件中设置的内容包括:
- 数据库的特殊设置,比如list路径、标注文件夹、真值文件夹等等;
- 数据库的统一设置,比如数据目录、标注模板、目标名字、colormap、尺寸、ignore_lable、类别范围等等。
- 任务特殊设定,比如路径、阈值、是否迭代、截图尺寸等。
- 一般设定,比如存储路径等普通路径、是否覆盖、是否保存结果、是否显示结果。
- 选择运行阶段,比如run_score_thresh、run_check_image_level_tags等。
- 是否生成实例真值。
注:目前只有cityscapes这一个数据库,其他数据库会显示"Unknown dataset option"。
下图为opts
的内容:
run_sub.m
对每张图片进行:
- 判断是否需要保存结果:如果结果已经存在且没有设置强制覆盖则忽略这张图。
- 用
load_data.m
(点击查看详细信息)加载必要的信息。 - 用
clean_label.m
(点击查看详细信息)文件处理原始估计,对语义分割的结果进行清理。 - 用
ins_box_process.m
(点击查看详细信息)文件创建实例标签。 - 每100张图汇报一下进度。
函数的输入是opts
,输出为新的opts
和估计结果results
,其中包括语义分割结果results.final_pred
、实例分割结果results.ins_pred
和实例信息results.ins_info
等。
输出示例:
results:
opts:
load_data.m
用于加载数据,包括真值等等,输出形如下图:
具体步骤:
- 如果
opts.run_score_thresh
为真,则获取估计权重存入results.pred_scores
。 - 如果
opts.run_apply_bbox_prior
、opts.run_check_low_iou
和opts.run_ins_box_process
这三个有一个为真,则读取bounding box信息存入results.gt_bbox_masks
和results.gt_bboxes
。 - 如果
opts.run_check_image_level_tags
为真,则读取真值信息存入results.gt_label
。 - 如果
opts.run_merge_with_mcg_and_grabcut
为真,则读取MCG和GrabCut的结果,存入results.mandg_pred
和results.mandg_cmap
。
visualise_results_cam_mandg.m
显示original prediction,original prediction scores, processed semantic iterative GT, and generated instance。
opts
(点击查看详细信息)中定义了用于显示的colormap。
clean_label.m
输入:
- 由
get_opts.m
(点击查看详细信息)生成的options - 包含必要数据和原始估计的struct
下图是作为输入的results:
输出: 包含必要数据、原始估计和处理估计的struct
下图是作为输出的results:
第一部分:Confidence threshold
(需要 pred_scores
+ prediction
+ conf_thresh
,如果opts.run_score_thresh
为真运行这一部分)
results.final_pred在那些results.pred_scores
小于opts.score_thresh
的位置处的值会被设成opts.ignore_label
。直观来说,就是把score小于阈值的位置的类别设成255。
下图是results.final_pred
中包含的所有类别:
第二部分:用crops的image tags对stuff做类别检查
(需要 gt_label
+ prediction
,如果opts.run_check_image_level_tags
为真运行这一部分)
调用函数check_image_level_tags
(点击查看详细信息),将结果存入results.pred_schk
,再赋值给results.final_pred
。
作为输入的估计标签是results.final_pred
,其中包括的标签形如:
真值形如:
具体流程看函数check_image_level_tags
的说明,其中被判断的是results.final_pred
,用于作为真值的是results.gt_label
。
第三部分:将处理好的估计与MCG&Grabcut融合
(如果opts.run_merge_with_mcg_and_grabcut
为真运行这一部分)
调用函数results.merge_mag_and_pred
(点击查看详细信息),输出存入results.pred_merge_w_mandg
,最终存入results.final_pred
。
第四部分:移除bounding boxes外的thing
(需要gt_bbox_masks
+ prediction
,如果opts.run_apply_bbox_prior
为真运行这一部分)
调用函数apply_bbox_prior.m
(点击查看详细信息),将没有bounding box的类别或在bounding box之外的点设成ignore。
第五部分,IoU检查,将低IoU的部分设成纯色(solid color)
(需要gt_bboxes
+ prediction
,如果opts.run_check_low_iou
为真运行这一部分)
调用函数check_low_iou.m
(点击查看详细信息),
ins_box_process.m
这个函数在给定语义分割真值估计的基础上,给出实例分割真值的估计。
认为thing group不属于实例。
具体步骤:
语义分割的ignore_label继承过来,也就是语义分割中定义为ignore,就还是ignore;
如果当前位置语义分割的结果对应当前类别,但是该位置上的实例分割已经有值了(比如是ignore或者之前迭代的时候已经赋值过),那么这个位置就设成ignore;否则,如果当前位置为0(没有值),就给该位置标注当前实例标签;
记录信息;
循环结束后检查,确保实例标注是连续的,不会跳过某个标注。
check_image_level_tags
这个文件检查prediction中与image-level tags相违背的部分。如果网络的prediction估计了一个class,但是其却没有显示标注的image-level tags,那么该估计label就会被换成ignore。
输入:
- 估计标签
- 用于生成image-level tags的真值
- 截取尺寸
- 图像尺寸
- ignore_label
输出:
修改后的估计标签
具体流程
将整张图片按照要截取的尺寸分成各个滑动窗口(有重叠,重叠部分的尺寸根据crop和图像尺寸计算,最终结果就是整个图像都被扫描过),然后依次对每个滑动窗口比较prediction和tags,判断是否prediction中出现过的所有类别都在对应的tags中存在,如果不存在,设成ignore。
merge_mag_and_pred.m
检查,要求mag和pred的尺寸要相同。
第一部分:若m&g label估计了thing类别标签C1
保留mag中所有与pred估计相同的部分 -> 保留mag中所有mag与pred都有thing估计,但是二者估计不同的部分 -> 保留mag中mag估计为thing但是pred中估计为背景的部分 -> 保留mag中估计为thing但是pred估计为ignore的部分。
注:这一部分相当于给了mag优先级,也就是说只要mag认为是thing就保留。
第二部分:若m&g label估计了背景类别0
若mag估计为背景但是pred估计为thing,则将该位置变成ignore -> 若mag和pred都认为是stuff(mag估计为背景),那么保留pred的该部分 -> 若mag认为背景但是pred标注ignore,则标注ignore。
注:即对于stuff类别,除非二者都认为是stuff,才保留pred,否则都认为是ignore。
第三部分:若mag估计为ignore(255)
若mag为ignore但是pred认为是thing,设为ignore -> 若mag认为ignore但pred认为是stuff,则保留pred -> 若二者都是ignore,则ignore。
apply_bbox_prior.m
对所有存在的thing类别遍历,如果该类别对于当前图像不存在bounding box,则将pred_label中对应该类别的位置设成ignore;若存在,找到所有该类别的位置,将不在bounding box中的位置设成ignore。
check_low_iou.m
对标注为ignore和属于stuff类别的位置不做处理;
默认小目标在大目标前面;
忽略stuff类别(因为stuff的标注是image-level的);
忽略thing group,因为thing group boxes通常有低precision高recall的特征,不适合这种后处理方式;
将IoU低于阈值的窗口内的所有点换成gt_class(可以设置不改变ignore和stuff部分)。
Cityscapes
按照Readme中的说明,如果在第二步时想运行batch_instanceTrainId_to_dets.m
这个文件,需要首先clone好cityscapes repository。这一部分介绍Cityscapes中的相关文件。
在Linux下运行的时候,clone好需要先到根目录下(有setup.py
的那个路径),运行命令sudo pip install .
,安装cityscapesscripts才可以,否则直接运行的时候会报错:“Importerror: No module named ‘cityscapes’.”
注:这个是在python2下的文件,如果用python3安装会报错(至少博主遇到的是这个情况)。修改系统默认的python版本的方法看这里。
在Windows系统下运行,首先需要到第26行,将open("README.md")
改成open("README.md",encodeing='UTF-8')
,否则会报错:unicodedecodeerror: 'gbk' codec can't decode byte ...
。然后在含有setup.py
文件的路径下打开命令行,输入python setup.py install
就可以了。安装这一步需要一些依赖项,如果没有它会自动安装,因此可能需要一段时间,耐心等待即可。
createTrainIdInstanceImgs.py
运行这个文件的时候要注意,下图中显示的CITYSCAPES_DATASET需要修改环境变量,存放cityscapes的路径。
加入星球了解更多分割知识: