目录
前言
在深度学习中,数据集一般是指用作网络训练的数据集合。数据集包含输入和真实输出(ground truth)两部分,视觉类深度学习中输入为图片,输出为分类结果、预测框及分割结果等。
数据集一般会分为训练集(train dataset)、验证集(valid dataset)和测试集(test dataset)三部分,训练集用于网络训练,验证集一般用于在训练是验证训练效果,根据训练效果保存训练最好的模型,测试集测试训练效果。
一、labelme标定
maskrcnn需要的输入为图片,输出为lable、box及mask。正常讲应该在标定的时候,label、box和mask都进行标定,但是实际上box可以由mask推出,所以只需要标定mask及label即可。
1.安装labelme
在anaconda中新建一个环境命名为lableme,安装labelme。
# python3
conda create --name=labelme python=3.6
source activate labelme
# install pyqt
pip install pyqt5 # pyqt5 can be installed via pip on python3
# instal labelme
pip install labelme
2.标记
cmd中或anaconda中:activate labelme,labeleme。打开labelme,create polygons,把需要检测分割的对象分割出来,打上标签。
一个个标注好了之后,将生成json文件。
二、对data进一步处理
1.数据增强
如果自己的数据比较少的话,为了避免过拟合等问题,最好进行下数据增强。可以加噪音,翻转等操作。
2.json_to_dataset
批量给它转成dataset
批量json to dataset,将生成四个文件,都放在一个文件夹下了。
也可以把里面的json_to_dataset.py文件复制到自己的项目中,修改一下,直接指定路径是自己标记好的Json文件夹,输出的路径也进一步修改下,可以把生成的dataset生成到项目文件夹下。修改如下:
import argparse
import base64
import json
import os
import os.path as osp
import imgviz
import PIL.Image
from labelme.logger import logger
from labelme import utils
def main():
# 输入路径
json_file = "./json_files"
# 为了批量处理的改动1,获得目录下所有的.json后缀文件
path = []
file_name = []
for root, dirs, files in os.walk(json_file): # 获取所有文件
for file in files: # 遍历所有文件名
if os.path.splitext(file)[1] == '.json': # 指定尾缀
file_name.append(file.split('.')[0]) # 为了获取**.json中的**
path.append(os.path.join(root, file)) # 拼接绝对路径并放入列表
print('总文件数目:', len(path))
# 为了批量处理改动2,都放入循环中
for i in range(len(path)):
data = json.load(open(path[i]))
imageData = data.get("imageData")
if not imageData:
imagePath = os.path.join(os.path.dirname(json_file), data["imagePath"])
with open(imagePath, "rb") as f:
imageData = f.read()
imageData = base64.b64encode(imageData).decode("utf-8")
img = utils.img_b64_to_arr(imageData)
label_name_to_value = {"_background_": 0}
for shape in sorted(data["shapes"], key=lambda x: x["label"]):
label_name = shape["label"]
if label_name in label_name_to_value:
label_value = label_name_to_value[label_name]
else:
label_value = len(label_name_to_value)
label_name_to_value[label_name] = label_value
lbl, _ = utils.shapes_to_label(
img.shape, data["shapes"], label_name_to_value
)
label_names = [None] * (max(label_name_to_value.values()) + 1)
for name, value in label_name_to_value.items():
label_names[value] = name
lbl_viz = imgviz.label2rgb(
label=lbl, img=imgviz.asgray(img), label_names=label_names, loc="rb"
)
# 输出路径
if not os.path.exists("train_dataset"):
os.mkdir("train_dataset")
mask_path = "train_dataset/mask"
if not os.path.exists(mask_path):
os.mkdir(mask_path)
img_path = "train_dataset/imgs"
if not os.path.exists(img_path):
os.mkdir(img_path)
class_path = "train_dataset/classes"
if not os.path.exists(class_path):
os.mkdir(class_path)
label_viz_path = "train_dataset/label_viz"
if not os.path.exists(label_viz_path):
os.mkdir(label_viz_path)
# 改动3:下面加了四个filename,1和2都是为了输出的时候名字改变
PIL.Image.fromarray(img).save(osp.join(img_path, file_name[i]+".png"))
utils.lblsave(osp.join(mask_path, file_name[i]+"_label.png"), lbl)
PIL.Image.fromarray(lbl_viz).save(osp.join(label_viz_path, file_name[i]+"_label_viz.png"))
with open(osp.join(class_path, file_name[i]+"label_names.txt"), "w") as f:
for lbl_name in label_names:
f.write(lbl_name + "\n")
logger.info("Saved {0} files".format(i+1))
if __name__ == "__main__":
main()
我的文件夹结构:
以上数据集的准备任务就完成了,下面是实际训练的时候所用的函数。
三、实际训练数据集处理
定义一个自己的数据集类,对处理过的train_dataset进行读取解析。
映射式数据集,需要定义__init__()和__getitem__()方法,之后利用torch.utitls.Dataloader()进行加载,分为一个个batch进行训练。
class PennFudanDataset(object):
def __init__(self, root, transforms):
self.root = root
self.transforms = transforms
# load all image files, sorting them to
# ensure that they are aligned
self.imgs = list(sorted(os.listdir(os.path.join(root, "images"))))
self.jsons = list(sorted(os.listdir(os.path.join(root, "jsons"))))
def __getitem__(self, idx):
# load images and masks
img_path = os.path.join(self.root, "images", self.imgs[idx])
json_path = os.path.join(self.root, "jsons", self.jsons[idx])
img = Image.open(img_path).convert("RGB")
mask = Image.open(json_path)
mask = np.array(mask)
# instances are encoded as different colors
obj_ids = np.unique(mask) # 原图被处理为,背景部分为0,第一个行人为1.第二个为2,以此类推。
# first id is the background, so remove it
obj_ids = obj_ids[1:]
# split the color-encoded mask into a set
# of binary masks
masks = mask == obj_ids[:, None, None]
# get bounding box coordinates for each mask
num_objs = len(obj_ids)
boxes = []
"""box是通过掩码得到的,那可以直接把mask改成抓取描述,由抓取描述也可以得到boxes吧"""
for i in range(num_objs):
pos = np.where(masks[i])
xmin = np.min(pos[1])
xmax = np.max(pos[1])
ymin = np.min(pos[0])
ymax = np.max(pos[0])
boxes.append([xmin, ymin, xmax, ymax])
boxes = torch.as_tensor(boxes, dtype=torch.float32)
# there is only one class
labels = torch.ones((num_objs,), dtype=torch.int64)
masks = torch.as_tensor(masks, dtype=torch.uint8)
image_id = torch.tensor([idx])
area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])
# suppose all instances are not crowd
iscrowd = torch.zeros((num_objs,), dtype=torch.int64)
target = {}
target["boxes"] = boxes
target["labels"] = labels
target["masks"] = masks
target["image_id"] = image_id
target["area"] = area
target["iscrowd"] = iscrowd
if self.transforms is not None:
img, target = self.transforms(img, target)
return img, target
def __len__(self):
return len(self.imgs)