系统:ubuntu16.04
内核:Linux 4.15.0-45-generic
python版本:3.5.2
pip install ninja yacs cython matplotlib opencv-python pillow sklearn tqdm utils
安装python3-tk
PyTorch from a nightly release. Installation instructions can be found in https://pytorch.org/get-started/locally/
按照官网给的指令安装
安装torchvision
pip install torchvision
安装pycocotools、apex
cd $INSTALL_DIR
git clone https://github.com/cocodataset/cocoapi.git
cd cocoapi/PythonAPI
python setup.py build_ext install
cd $INSTALL_DIR
git clone https://github.com/NVIDIA/apex.git
cd apex
python setup.py install --cuda_ext --cpp_ext
cuda:
download: https://developer.nvidia.com/cuda-toolkit-archive
installation guide: https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html
sudo dpkg -i cuda-repo-ubuntu1604<<>>
sudo apt-key add /var/cuda-repo-<<>>/7fa2af80.pub
sudo apt-get update
sudo apt-get install cuda
安装完成后,将以下内容加到~/.bashrc最后
export PATH=/usr/local/cuda-9.0/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64:$LD_LIBRARY_PATH
检查cuda安装是否成功:
nvcc -V
若输出cuda版本信息,代表正常
#编译并测试设备 deviceQuery:
cd /usr/local/cuda-9.2/samples/1_Utilities/deviceQuery
sudo make
./deviceQuery
#编译并测试带宽 bandwidthTest:
cd ../bandwidthTest
sudo make
./bandwidthTest
若这两个测试的最后结果都是Result = PASS,说明cuda安装成功。
若已有其他版本cuda,可选择安装多个版本。安装时需要指定版本,如安装cuda10.0:
sudo apt-get install cuda-10-0
注意修改.bashrc
切换默认的cuda版本:
sudo rm -rf cuda
sudo ln -s /usr/local/cuda- /usr/local/cuda
cudnn(optional):
download (need a nvidia account): https://developer.nvidia.com/rdp/cudnn-archive
安装:如果下载的文件是solitairetheme8格式,执行
cp cudnn--linux-x64-v.solitairetheme8 cudnn--linux-x64-v.tgz
执行:
tar -xvf cudnn-<<>>
sudo cp cuda/include/cudnn.h /usr/local/cuda/include/
sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64/
sudo chmod a+r /usr/local/cuda/include/cudnn.h
sudo chmod a+r /usr/local/cuda/lib64/libcudnn*
查看已安装版本:
cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2
安装cudnn的Runtime Library , Develop Library , Code Samples and User Guide:
sudo dpkg -i *.deb
cd $INSTALL_DIR
git clone https://github.com/facebookresearch/maskrcnn-benchmark.git
cd maskrcnn-benchmark
python setup.py build develop
unset INSTALL_DIR
到此,maskrcnn-benchmark安装完成。
maskrcnn提供了测试用模型,该模型共提供了80种物品的识别,而且精度也是比较好的,可以通过以下代码执行(首次执行需要联网下载模型):
cd $INSTALL_DIR/maskrcnn-benchmark/demo
sudo python3 webcam.py
如果你的电脑自带或已连接摄像头,它将调取默认摄像头,基于模型对视频做物品的识别,并将识别结果显示在屏幕上。
官方给的模型再好,大部分情况下还是不能满足我们的使用需求,所以这时就需要我们自己训练模型,用于我们特定的任务。
首先准备出我们待识别物品的图片,最好是多角度的。然后把它放到一个文件夹里
安装工具labelme:
pip install labelme
打开labelme:
labelme
选择放图片的文件夹,载入图片。对每张图片上待辨识的物品,点击Create Polygons,将每个待识别的物品用多边形描绘出闭合边框,闭合后打上标签。当该图片上的所有待识别物品都已经打好标签,点击Save保存,就会将该图片的信息保存为一个同名的json文件。
将刚刚整个文件夹命名为’labelme’,下载或复制代码到labelme上层:
代码参考:https://github.com/spytensor/prepare_detection_dataset
#https://github.com/spytensor/prepare_detection_dataset
#coding:utf-8
import os
import json
import numpy as np
import glob
import shutil
from sklearn.model_selection import train_test_split
np.random.seed(41)
#0为背景
classname_to_id = {"vita_tea": 1, "biscuit": 2, "crisps": 3, "chocolate": 4, "lemon_tea": 5, "coke_cola": 6, "chew_gum": 7}
class Lableme2CoCo:
def __init__(self):
self.images = []
self.annotations = []
self.categories = []
self.img_id = 0
self.ann_id = 0
def save_coco_json(self, instance, save_path):
json.dump(instance, open(save_path, 'w', encoding='utf-8'), ensure_ascii=False, indent=1) # indent=2 更加美观显示
# 由json文件构建COCO
def to_coco(self, json_path_list):
self._init_categories()
for json_path in json_path_list:
obj = self.read_jsonfile(json_path)
self.images.append(self._image(obj, json_path))
shapes = obj['shapes']
for shape in shapes:
annotation = self._annotation(shape)
self.annotations.append(annotation)
self.ann_id += 1
self.img_id += 1
instance = {}
instance['info'] = 'spytensor created'
instance['license'] = ['license']
instance['images'] = self.images
instance['annotations'] = self.annotations
instance['categories'] = self.categories
return instance
# 构建类别
def _init_categories(self):
for k, v in classname_to_id.items():
category = {}
category['id'] = v
category['name'] = k
self.categories.append(category)
# 构建COCO的image字段
def _image(self, obj, path):
image = {}
from labelme import utils
img_x = utils.img_b64_to_arr(obj['imageData'])
h, w = img_x.shape[:-1]
image['height'] = h
image['width'] = w
image['id'] = self.img_id
image['file_name'] = os.path.basename(path).replace(".json", ".jpg")
return image
# 构建COCO的annotation字段
def _annotation(self, shape):
label = shape['label']
points = shape['points']
annotation = {}
annotation['id'] = self.ann_id
annotation['image_id'] = self.img_id
annotation['category_id'] = int(classname_to_id[label])
annotation['segmentation'] = [np.asarray(points).flatten().tolist()]
annotation['bbox'] = self._get_box(points)
annotation['iscrowd'] = 0
annotation['area'] = 1.0
return annotation
# 读取json文件,返回一个json对象
def read_jsonfile(self, path):
with open(path, "r", encoding='utf-8') as f:
return json.load(f)
# COCO的格式: [x1,y1,w,h] 对应COCO的bbox格式
def _get_box(self, points):
min_x = min_y = np.inf
max_x = max_y = 0
for x, y in points:
min_x = min(min_x, x)
min_y = min(min_y, y)
max_x = max(max_x, x)
max_y = max(max_y, y)
return [min_x, min_y, max_x - min_x, max_y - min_y]
if __name__ == '__main__':
labelme_path = "./labelme/"
saved_coco_path = "./"
# 创建文件
if not os.path.exists("%scoco/annotations/"%saved_coco_path):
os.makedirs("%scoco/annotations/"%saved_coco_path)
if not os.path.exists("%scoco/images/train2017/"%saved_coco_path):
os.makedirs("%scoco/images/train2017"%saved_coco_path)
if not os.path.exists("%scoco/images/val2017/"%saved_coco_path):
os.makedirs("%scoco/images/val2017"%saved_coco_path)
# 获取images目录下所有的json文件列表
json_list_path = glob.glob(labelme_path + "/*.json")
# 数据划分,这里没有区分val2017和train2017目录,所有图片都放在images目录下
train_path, val_path = train_test_split(json_list_path, test_size=0.12)
print("train_n:", len(train_path), 'val_n:', len(val_path))
# 把训练集转化为COCO的json格式
l2c_train = Lableme2CoCo()
train_instance = l2c_train.to_coco(train_path)
l2c_train.save_coco_json(train_instance, '%scoco/annotations/instances_train2017.json'%saved_coco_path)
for file in train_path:
shutil.copy(file.replace("json","jpg"),"%scoco/images/train2017/"%saved_coco_path)
for file in val_path:
shutil.copy(file.replace("json","jpg"),"%scoco/images/val2017/"%saved_coco_path)
# 把验证集转化为COCO的json格式
l2c_val = Lableme2CoCo()
val_instance = l2c_val.to_coco(val_path)
l2c_val.save_coco_json(val_instance, '%scoco/annotations/instances_val2017.json'%saved_coco_path)
其中需要修改的地方就是最开始的classname_to_id那个字典,需要按照示例的形式将标签与序号对应起来,完成后执行代码,就会在代码同目录下生成一个coco文件夹,这就是我们需要的coco数据集。
在maskrcnn-benchmark下创建文件夹datasets,将coco整个文件夹放入datasets中。
在maskrcnn-benchmark/maskrcnn-benchmark/paths_catalog.py中有训练集的名字、图片路径和生成的注解。这里需要把路径修改到刚刚的coco文件夹位置。这里建议都用绝对路径,一般文件没找到的就是相对路径的锅。
接下来在maskrcnn-benchmark/config下有一些配置文件,我比较常用的是e2e_mask_rcnn_R_101_FPN_1x.yaml,如果电脑配置不太好的话可以改用e2e_mask_rcnn_R_50_FPN_1x.yaml。这里也有一些东西需要增加或修改,这边贴一个我用的配置,有注释的地方是需要注意的
MODEL:
META_ARCHITECTURE: "GeneralizedRCNN"
# 如果是新开始的训练,WEIGHT置空即可。如果接着训练这里要给出模型的路径
WEIGHT: ‘’
BACKBONE:
CONV_BODY: "R-101-FPN"
RESNETS:
BACKBONE_OUT_CHANNELS: 256
RPN:
USE_FPN: True
ANCHOR_STRIDE: (4, 8, 16, 32, 64)
PRE_NMS_TOP_N_TRAIN: 2000
PRE_NMS_TOP_N_TEST: 1000
POST_NMS_TOP_N_TEST: 1000
FPN_POST_NMS_TOP_N_TEST: 1000
ROI_HEADS:
USE_FPN: True
ROI_BOX_HEAD:
POOLER_RESOLUTION: 7
POOLER_SCALES: (0.25, 0.125, 0.0625, 0.03125)
POOLER_SAMPLING_RATIO: 2
FEATURE_EXTRACTOR: "FPN2MLPFeatureExtractor"
PREDICTOR: "FPNPredictor"
# 这里要给出待辨识的物体数+1(因为有背景)
NUM_CLASSES: 4
ROI_MASK_HEAD:
POOLER_SCALES: (0.25, 0.125, 0.0625, 0.03125)
FEATURE_EXTRACTOR: "MaskRCNNFPNFeatureExtractor"
PREDICTOR: "MaskRCNNC4Predictor"
POOLER_RESOLUTION: 14
POOLER_SAMPLING_RATIO: 2
RESOLUTION: 28
SHARE_BOX_FEATURE_EXTRACTOR: False
MASK_ON: True
DATASETS
# 这里给出训练集的名字,要和paths_catalog.py的名字对应起来
# 同时要保留括号里的最后一个逗号
TRAIN: ("coco_2017_train",)
TEST: ("coco_2017_val",)
DATALOADER:
NUM_WORKERS: 0
SIZE_DIVISIBILITY: 32
SOLVER
# 基础学习率,如果模型不收敛(loss是nan)就要把这里改小一点
# 如果太小会导致训练进度非常缓慢
BASE_LR: 0.002
WEIGHT_DECAY: 0.0001
STEPS: (60000, 80000)
# 这是最大迭代次数,训练这么多轮之后会自动停止
MAX_ITER: 100000
# 这里是每批训练的图片数。如果爆显存了(什么什么overflow)就把这里改小
IMS_PER_BATCH: 1
# 这是存档点,就是每训练多少轮存一次当前模型
CHECKPOINT_PERIOD: 1000
# 这是存档和最终模型的保存路径,可以随意设置
OUTPUT_DIR: "/home/syq/github/maskrcnn-benchmark/weight
其实还会遇到一些错误,不过看得懂英文应该都可以改掉。我记得还有一个Syntax Error是因为一个地方前面多了一个f,删掉就好了
接下来就可以开始训练了。我一般习惯把这个文件放在tools/下面,因为不会和使用时的配置文件搞混。要引用这个配置文件的内容,可以在train_net.py中config_files部分的default处填上这个文件的路径,或者直接在命令中给出
sudo python3 ./tools/train_net.py --config-file ./tools/config_files/e2e_mask_rcnn_R_101_FPN_1x.yaml
训练当中可以强制停止,已经过的存档点模型将会保存到预设路径下,没有保存的进度将会丢失,之后仍可以通过载入旧模型继续训练。
复制一份配置文件到./demo/下,把路径改为刚训练好的模型的路径,并且在predictor.py中把CATEGORIES修改为你训练的标签(在存放模型的地方有一个文件给出了标签的顺序,按照那个顺序填入标签),要保留__background标签。
然后就可以运行webcam.py,并引入这个配置文件来实际测试训练模型的效果如何。
在上面的步骤中,我们只能得到一张分隔好的图片,如果要借助分割信息来做一些自动化的工作,就必须知道图片上有哪些物品,可能还需要知道它们在哪里。
要达成这个目的,我们需要从原来的代码中将这项信息return回来。
./demo/predictor.py中,194行的run_on_opencv_image(self, image)函数中的top_predictions就是我们需要的信息。在217行的return语句中,我们将它连同result一起return回来,即将return语句修改为
return result, top_predictions
同时需要修改webcam.py来显示这些信息或者对其做进一步的分析和操作。这里因为我不太想对源文件做改动,所以重写了该文件,有一些不必要的也略去了,仅供参考。
# my_predictor_camera.py
# coding:utf-8
from maskrcnn_benchmark.config import cfg
from predictor import COCODemo
import argparse
import torch
import time
import cv2
import os
config_file = "/home/syq/github/maskrcnn-benchmark/demo/config_files/e2e_mask_rcnn_R_101_FPN_1x_caffe2.yaml"
# update the config options with the config file
cfg.merge_from_file(config_file)
# manual override some options
cfg.merge_from_list(["MODEL.DEVICE", "cuda"]);
coco_demo = COCODemo(
cfg,
min_image_size = 800,
confidence_threshold = 0.7,
)
capture = cv2.VideoCapture(0)
while True:
start_time = time.time()
ret, img = capture.read()
img = cv2.flip(img, 1)
composite, top_predictions = coco_demo.run_on_opencv_image(img)
scores = top_predictions.get_field("scores").tolist()
boxes = top_predictions.bbox
labels = top_predictions.get_field("labels").tolist()
for score, box, label in zip(scores, boxes, labels):
box = box.to(torch.int64)
top_left, bottom_right = box[:2].tolist(), box[2:].tolist()
print(label, score, [top_left, bottom_right])
print("Time: {:.2f} s / img".format(time.time() - start_time))
cv2.imshow("COCO detections", composite)
if cv2.waitKey(1) == 27:
break # esc to quit
cv2.destroyAllWindows()
这里的 coco_demo.run_on_opencv_image(img)返回结果改为:composite和top_predictions,这就是我们新拿到手的信息。经过这里的处理,我们可以对每帧图片中的每个物品的编号、坐标、识别概率打印到终端中,也可以通过编号、编号和标签的对应关系拿到物品的标签,以开展更多的用途。