win11配置Mask DINO踩坑记录
前情提要:需要复现Mask DINO,但是实验室没有Linux的电脑,在Windows上复现的时候遇到了 No moduld name MultiScaleDeformableAttention,然后看博客说在Windows上配置很麻烦,建议去Linux,但是虚拟机没办法用GPU,想到了三个方法:1、用WSL2来运行;2、在Windows上解决这个困难;3、装双系统。
我首先试了方法1,碰到了一开始的问题,而且也比较麻烦,所以干脆进行了2,网上一些方法写的比较简略,但是我实际运行过程中碰到了很多问题,所以记录一下。
PS:如果是已经conda create一个环境了,建议查看版本是否正确,如果是直接在项目下运行了
pip install -r requirement.txt
,就重头开始
参考:Mask DINO环境配置
参考:win10系统配置Mask DINO经验总结
参考:Detectron2在Windows上的三部曲——安装
[参考:panopticapi库的安装](https://blog.youkuaiyun.com/None__None/article/details/135442575)
参考:【实例分割(一)】Detectron2 数据集制作并注册数据集训练
参考:detectron2 中 demo 可视化添加置信度阈值
1 准备工作
名称 | 版本号 |
---|---|
cuda | 11.3 |
cudnn | cuda11对应最新版即可 |
pytorch | 1.10.1 |
torchvision | 0.11.2 |
torchaudio | 0.10.1 |
2 创建python环境和安装detectron2
conda create -n env_name python=3.9
conda activate env_name
conda install pytorch==1.10.1 torchvision==0.11.2 torchaudio==0.10.1 cudatoolkit=11.3 -c pytorch -c conda-forge
#安装detectron2
git clone https://github.com/facebookresearch/detectron2.git
python -m pip install -e detectron2
如果conda安装慢,记得换源
安装detectron2开始出问题了
2.1 安装前提
需要安装Visual Studio,VS的版本一定要version<=2019,之前安装的VS2022死活安不上。
并且一定要把原来的VS2022删掉,否则会报错,安装了VS2019需要配置cl.exe的环境变量,请看2.2.1
2.2 安装流程
git clone https://github.com/facebookresearch/detectron2.git
Detectron2前将fvcore安装上
pip install fvcore
python setup.py install
成功安装结果:
2.2.1 cl.exe的错误
至此安装完成detectron2,我一开始没有安装,所以报错'cl.exe' is not recognized as an internal or external command
这是因为VS的cl.exe这个编译器未被加入到环境变量中,可以“编辑环境变量-环境变量-系统变量-PATH“, 添加路径**“path\to\vs2019\community\vc\tools\msvc\14.13.26128\bin\hostx64\x64"** 大概是这个路径,自行去找自己的路径。(VS2022是没有这个路径的)
安装完detectron2,再继续安装mask dino一开始遇到的问题都解决了
2.2.2 SetuptoolsDeprecationWarning的错误
错误内容:
SetuptoolsDeprecationWarning: setup.py install is deprecated. !! ********************************************************************************
Please avoid running setup.py directly. Instead, use pypa/build, pypa/installer or other standards-based tools. See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.
**解决:我的原因是setuptools版本太高
首先使用以下命令查看setuptools版本信息
conda list
发现setuptools版本大于60,可能因为版本过高,算法使用的安装方式已经被弃用,选择重新安装小于60的版本,解决了此问题。
pip install setuptools==58
**
3 MaskDINO运行
#克隆maskdino库
git clone https://github.com/IDEA-Research/MaskDINO.git
cd ./MaskDINO
pip install -r requirements.txt
注意是requirements.txt,有无s
成功结果:
再接下来
cd maskdino/modeling/pixel_decoder/ops
python setup.py build install
(这里碰到了没有删除vs2022遇到的错,卸载了就没有报错了)
运行的时候出现了一大串类似于报错的信息,但是等待就好了
成功结果:
3.1 运行demo
退回到根目录 \MaskDINO-main,在根目录创建img和weight文件夹,并在img中放入测试图片,weight中放入权重:自行下载
python .\demo\demo.py --config-file .\configs\coco\instance-segmentation\maskdino_R50_bs16_50ep_3s.yaml --input .\img\1.jpg --output .\img\2.jpg --opts MODEL.WEIGHTS .\weight\maskdino_r50_50ep_300q_hid1024_3sd1_instance_maskenhanced_mask46.1ap_box51.5ap.pth
4 训练自己的数据集
4.1 panopticapi库的安装
不能直接用上述代码安装panopticapi库
方法:
首先克隆代码
git clone https://github.com/cocodataset/panopticapi.git
目录在项目同文件夹就行
终端进入conda创建的虚拟环境,并用cd panopticapi
进入文件夹
python setup.py build_ext --inplace
python setup.py build_ext install
4.2 训练自己标注的labelme数据集
前置条件:已经将数据集转换成coco格式的数据集了
没有转换,请看博客:labelme标注的json文件数据转成coco数据集格式(可处理目标框和实例分割)
条件:只用自己的笔记本电脑 8G GPU跑
在train_net.py文件下加入注册数据集的代码,在main函数上方添加即可
class Register:
"""用于注册自己的数据集"""
CLASS_NAMES = ['__background__', '1']
ROOT = r"D:\ 图片所在目录"
def __init__(self):
self.CLASS_NAMES = Register.CLASS_NAMES
# 数据集路径
self.ANN_ROOT = r"D:\ 图片所在目录\annotations"
self.TRAIN_PATH = os.path.join(Register.ROOT, 'train')
self.VAL_PATH = os.path.join(Register.ROOT, 'val')
self.TRAIN_JSON = os.path.join(self.ANN_ROOT, 'annotations_train.json')
self.VAL_JSON = os.path.join(self.ANN_ROOT, 'annotations_val.json')
# TODO声明数据集的子集,需修改成自己的
self.PREDEFINED_SPLITS_DATASET = {
"coco_fish_train": (self.TRAIN_PATH, self.TRAIN_JSON),
"coco_fish_val": (self.VAL_PATH, self.VAL_JSON),
}
def register_dataset(self):
""" purpose: register all splits of datasets with PREDEFINED_SPLITS_DATASET 注册数据集(这一步就是将自定义数据集注册进Detectron2) """
for key, (image_root, json_file) in self.PREDEFINED_SPLITS_DATASET.items():
self.register_dataset_instances(name=key,
json_file=json_file,
image_root=image_root)
@staticmethod
def register_dataset_instances(name, json_file, image_root):
""" purpose: register datasets to DatasetCatalog, register metadata to MetadataCatalog and set attribute 注册数据集实例,加载数据集中的对象实例 """
DatasetCatalog.register(name, lambda: load_coco_json(json_file, image_root, name))
MetadataCatalog.get(name).set(json_file=json_file,
image_root=image_root,
evaluator_type="coco")
def plain_register_dataset(self):
"""注册数据集和元数据"""
# 训练集
DatasetCatalog.register("coco_fish_train", lambda: load_coco_json(self.TRAIN_JSON, self.TRAIN_PATH))
MetadataCatalog.get("coco_fish_train").set(thing_classes=self.CLASS_NAMES, # 可以选择开启,但是不能显示中文,这里需要注意,中文的话最好关闭
evaluator_type='coco', # 指定评估方式
json_file=self.TRAIN_JSON,
image_root=self.TRAIN_PATH)
# DatasetCatalog.register("coco_my_val", lambda: load_coco_json(VAL_JSON, VAL_PATH, "coco_2017_val"))
# 验证/测试集
DatasetCatalog.register("coco_fish_val", lambda: load_coco_json(self.VAL_JSON, self.VAL_PATH))
MetadataCatalog.get("coco_fish_val").set(thing_classes=self.CLASS_NAMES, # 可以选择开启,但是不能显示中文,这里需要注意,中文的话最好关闭
evaluator_type='coco', # 指定评估方式
json_file=self.VAL_JSON,
image_root=self.VAL_PATH)
def checkout_dataset_annotation(self, name="coco_fish_val"):
""" 查看数据集标注,可视化检查数据集标注是否正确, 这个也可以自己写脚本判断,其实就是判断标注框是否超越图像边界 可选择使用此方法 """
# dataset_dicts = load_coco_json(TRAIN_JSON, TRAIN_PATH, name)
dataset_dicts = load_coco_json(self.TRAIN_JSON, self.TRAIN_PATH)
print(len(dataset_dicts))
for i, d in enumerate(dataset_dicts, 0):
# print(d)
img = cv2.imread(d["file_name"])
visualizer = Visualizer(img[:, :, ::-1], metadata=MetadataCatalog.get(name), scale=1.5)
vis = visualizer.draw_dataset_dict(d)
# cv2.imshow('show', vis.get_image()[:, :, ::-1])
cv2.imwrite('./out/' + str(i) + '.jpg', vis.get_image()[:, :, ::-1])
# cv2.waitKey(0)
if i == 100:
break
然后在配置文件yaml中修改train和val的名字,名字为自己定义的,运行
添加参数:--num-gpus 1 --config-file .\configs\coco\instance-segmentation\maskdino_R50_bs16_50ep_3s.yaml
至此,运行train_net.py结束。
4.3 可视化后图片效果不好改进方法
使用demo.py可视化后,得到的结果并不好,会出现很多置信度很低的框。
:
1、设置置信度阈值(score_thredshold)部分代码
if score_threshold != None:
top_id = np.where(scores.numpy()>score_threshold)[0].tolist()
scores = torch.tensor(scores.numpy()[top_id])
boxes.tensor = torch.tensor(boxes.tensor.numpy()[top_id])
classes = [classes[ii] for ii in top_id]
labels = [labels[ii] for ii in top_id]
draw_instance_predictions 函数完整代码(修改后)
只需要把上面的代码复制粘帖到if predictions.has(“pred_masks”):之前就可以了
这个函数的位置是在 detectron2/utils/visualizer.py 里面
以上是博客的内容,只修改博客的内容会报错mask的数量与instance对不上,所以还要修改mask
最终代码
def draw_instance_predictions(self, predictions, score_threshold=None, jittering: bool = True):
"""
Draw instance-level prediction results on an image.
Args:
score_threshold: 置信度阈值(新增的参数)
predictions (Instances): the output of an instance detection/segmentation
model. Following fields will be used to draw:
"pred_boxes", "pred_classes", "scores", "pred_masks" (or "pred_masks_rle").
jittering: if True, in color mode SEGMENTATION, randomly jitter the colors per class
to distinguish instances from the same class
Returns:
output (VisImage): image object with visualizations.
"""
boxes = predictions.pred_boxes if predictions.has("pred_boxes") else None
scores = predictions.scores if predictions.has("scores") else None
classes = predictions.pred_classes.tolist() if predictions.has("pred_classes") else None
labels = _create_text_labels(classes, scores, self.metadata.get("thing_classes", None))
keypoints = predictions.pred_keypoints if predictions.has("pred_keypoints") else None
top_id=[]
# TODO:SYT
'''新增的部分代码'''
if score_threshold != None:
top_id = np.where(scores.numpy() > score_threshold)[0].tolist()
scores = torch.tensor(scores.numpy()[top_id])
boxes.tensor = torch.tensor(boxes.tensor.numpy()[top_id])
classes = [classes[ii] for ii in top_id]
labels = [labels[ii] for ii in top_id]
if predictions.has("pred_masks"):
# TODO: 不修改mask会报错
if len(top_id)!=0:
mask_id = np.asarray(predictions.pred_masks)
masks = [mask_id[i] for i in top_id]
masks = [GenericMask(x, self.output.height, self.output.width) for x in masks]
else:
masks = np.asarray(predictions.pred_masks)
masks = [GenericMask(x, self.output.height, self.output.width) for x in masks]
else:
masks = None
if self._instance_mode == ColorMode.SEGMENTATION and self.metadata.get("thing_colors"):
colors = [
self._jitter([x / 255 for x in self.metadata.thing_colors[c]]) for c in classes
] if jittering else [
tuple(
mplc.to_rgb([x / 255 for x in self.metadata.thing_colors[c]])
) for c in classes
]
alpha = 0.8
else:
colors = None
alpha = 0.5
if self._instance_mode == ColorMode.IMAGE_BW:
self.output.reset_image(
self._create_grayscale_image(
(predictions.pred_masks.any(dim=0) > 0).numpy()
if predictions.has("pred_masks")
else None
)
)
alpha = 0.3
self.overlay_instances(
masks=masks,
boxes=boxes,
labels=labels,
keypoints=keypoints,
assigned_colors=colors,
alpha=alpha,
)
return self.output
在demo下的predictions.py文件中修改run_on_image函数,加上阈值
def run_on_image(self, image):
"""
Args:
image (np.ndarray): an image of shape (H, W, C) (in BGR order).
This is the format used by OpenCV.
Returns:
predictions (dict): the output of the model.
vis_output (VisImage): the visualized image output.
"""
vis_output = None
predictions = self.predictor(image)
# Convert image from OpenCV BGR format to Matplotlib RGB format.
image = image[:, :, ::-1]
visualizer = Visualizer(image, self.metadata, instance_mode=self.instance_mode)
if "panoptic_seg" in predictions:
panoptic_seg, segments_info = predictions["panoptic_seg"]
vis_output = visualizer.draw_panoptic_seg_predictions(
panoptic_seg.to(self.cpu_device), segments_info
)
else:
if "sem_seg" in predictions:
vis_output = visualizer.draw_sem_seg(
predictions["sem_seg"].argmax(dim=0).to(self.cpu_device)
)
if "instances" in predictions:
instances = predictions["instances"].to(self.cpu_device)
# TODO:阈值0.5
vis_output = visualizer.draw_instance_predictions(predictions=instances, score_threshold=0.5)
return predictions, vis_output
结果
4.4 使用tensorboard可视化loss
进入到根目录,打开cmd,进入创建的conda环境,
安装tensorboard
pip install tensorboard
在根目录,运行OUTPUT_DIR是weight输出的目录,tensorboard用的是
tensorboard --logdir=“OUTPUT_DIR”