【Tool】Augmentor和imgaug——python图像数据增强库
Augmentor和imgaug——python图像数据增强库
Tags: ComputerVision Python
介绍两个图像增强库:Augmentor和imgaug,Augmentor使用比较简单,只有一些简单的操作。 imgaug实现的功能更多,可以对keypoint, bounding box同步处理,比如你现在由一些标记好的数据,只有同时对原始图片和标记信息同步处理,才能有更多的标记数据进行训练。我在segmentation和detection任务经常使用imgaug这个库。
Augmentor
http://augmentor.readthedocs.io/en/master/index.html
Augmentor 是管道化的图像增强库,每一个增强操作都是逐步叠加在图像上。此外对于输入的图像,可以选择按照一定的概率进行增强,比如只随机对一半图像进行旋转。
rotate(probability=0.5, max_left_rotation=5, max_right_rotation=10)
可以实现的操作有, rotate, crop, perspective skew(视角倾斜), elastic distortions(弹性变换), sheering(坐标轴倾斜), mirroring(镜像)
可以使用Augumentor.Pipeline()创建一个实例,调用各种方法向pipeline添加方法, status()可以显示当前pipeline的状态,在status中每个操作都有一个对应的index, remove_operation(index)移除一个操作, 最后调用sample(nums)得到nums个augument后的图像。
import Augmentor
p = Augmentor.Pipeline("/path/to/images/")
p.status()
p.remove_operation(0)
rotate
- rotate() 旋转,非90度旋转会带来padding或者裁剪
- rotate90()
- rotate180()
- rotate270()
- rotate_random_90() 随机旋转,90, 180, 270
resize
crop
- crop_centre()
- crop_by_size()
- crop_random()
sheer
+ sheer()
mirroring
- flip_left_right()
- flip_top_bottom()
- flip_random()
elastic distortion
- random_distortion()
Before
After
弹性变换是在计算机视觉任务中经常使用的一种变换,比较有名的Segmentation Model U-Net就使用了elastic deformation来对自己的数据做Augmentation.最后取得了较好的效果.
imgaug
http://imgaug.readthedocs.io/en/latest/index.html
安装
依赖
- numpy
- scipy
- scikit-image (pip install -U + scikit-image)
- six (pip install -U six)
- OpenCV (i.e. cv2 must be available in python). The library is mainly tested in OpenCV 2, but seems to also work in OpenCV 3.
pip install git+https://github.com/aleju/imgaug
或者
pip install imgaug
前者安装github最新版本,后者安装pypi版本。
basic
Keypoint
Bounding Boxes
这个部分做object detection的人应该经常用到。
imgaug支持:
- 将bounding boxes作为对象表示
- 增强boundiing boxes
- 在图像上画bounding boxes
- boxing boxes移动, 映射, 计算IoU
Before
After
由于VOC_PASCAL是在分割和检测领域常见的数据集,这里给出一个使用VOC_PASCAL标记格式进行数据增强的例子。
标记格式:
<?xml version="1.0" ?>
<annotation>
<folder>Pictures</folder>
<filename>bndbox.jpg</filename>
<path>/home/redtea/Pictures/bndbox.jpg</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>1200</width>
<height>1200</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>cat</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>49</xmin>
<ymin>647</ymin>
<xmax>599</xmax>
<ymax>1125</ymax>
</bndbox>
</object>
<object>
<name>dog</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>678</xmin>
<ymin>547</ymin>
<xmax>1159</xmax>
<ymax>1159</ymax>
</bndbox>
</object>
</annotation>
import xml.etree.ElementTree as ET
import pickle
import os
from os import getcwd
import numpy as np
from PIL import Image
import cv2
import imgaug as ia
from imgaug import augmenters as iaa
ia.seed(1)
def read_xml_annotation(root,image_id):
in_file = open(os.path.join(root,image_id))
tree = ET.parse(in_file)
root = tree.getroot()
bndbox = root.find('object').find('bndbox')
xmin = int(bndbox.find('xmin').text)
xmax = int(bndbox.find('xmax').text)
ymin = int(bndbox.find('ymin').text)
ymax = int(bndbox.find('ymax').text)
return (xmin, ymin, xmax, ymax)
def change_xml_annotation(root, image_id, new_target):
new_xmin = new_target[0]
new_ymin = new_target[1]
new_xmax = new_target[2]
new_ymax = new_target[3]
in_file = open(os.path.join(root, str(image_id)+'.xml')) #这里root分别由两个意思
tree = ET.parse(in_file)
xmlroot = tree.getroot()
object = xmlroot.find('object')
bndbox = object.find('bndbox')
xmin = bndbox.find('xmin')
xmin.text = str(new_xmin)
ymin = bndbox.find('ymin')
ymin.text = str(new_ymin)
xmax = bndbox.find('xmax')
xmax.text = str(new_xmax)
ymax = bndbox.find('ymax')
ymax.text = str(new_ymax)
tree.write(os.path.join(root,str(image_id)+"_aug"+'.xml'))
if __name__ == "__main__":
cmd = os.getcwd()
image_id = "bndbox"
img = Image.open(os.path.join(cmd, str(image_id)+'.jpg'))
img = np.array(img)
bndbox = read_xml_annotation(cmd, str(image_id)+'.xml')
bbs = ia.BoundingBoxesOnImage([
ia.BoundingBox(x1=bndbox[0], y1=bndbox[1], x2=bndbox[2], y2=bndbox[3])
], shape=img.shape)
seq = iaa.Sequential([
iaa.Flipud(0.5), # vertically flip 20% of all images
iaa.Multiply((1.2, 1.5)), # change brightness, doesn't affect BBs
iaa.Affine(
translate_px={"x": 10, "y": 10},
scale=(0.8, 0.95),
rotate=(-10,10)
) # translate by 40/60px on x/y axis, and scale to 50-70%, affects BBs
])
seq_det = seq.to_deterministic() # 保持坐标和图像同步改变,而不是随机
image_aug = seq_det.augment_images([img])[0]
bbs_aug = seq_det.augment_bounding_boxes([bbs])[0]
before = bbs.bounding_boxes[0]
after = bbs_aug.bounding_boxes[0]
print("BB : (%.4f, %.4f, %.4f, %.4f) -> (%.4f, %.4f, %.4f, %.4f)" % (
before.x1, before.y1, before.x2, before.y2,
after.x1, after.y1, after.x2, after.y2)
)
image_before = bbs.draw_on_image(img, thickness=2)
image_after = bbs_aug.draw_on_image(image_aug, thickness=2)
Image.fromarray(image_before).save("before.jpg")
Image.fromarray(image_after).save('after.jpg')
new_bndbox = []
new_bndbox.append(int(bbs_aug.bounding_boxes[0].x1))
new_bndbox.append(int(bbs_aug.bounding_boxes[0].y1))
new_bndbox.append(int(bbs_aug.bounding_boxes[0].x2))
new_bndbox.append(int(bbs_aug.bounding_boxes[0].y2))
# 修改xml tree 并保存
change_xml_annotation(cmd, image_id, new_bndbox)
这个包好像不能画出倾斜的bounding box, 我的read xml程序只能读取第一个bounding box,懒得修改了。
总之我觉得如果你Augmentor不能满足你就可以使用imgaug,但是有一点需要注意!imgaug中一些变换会给边缘区域带来黑色填充块,如果这些黑色填充块对你的模型有影响的话,就需要特殊处理!!