非常详细的Kaggle实战(二)图片预处理
图片预处理
对图片进行以下操作:
1、转化为黑白图像
2、进行一系列仿射变换
一、转化为黑白图像
在早期实验中,作者发现当比较两个彩色图像或两个黑白图像时,模型能够达到了差不多相同的精度。 但是,将彩色图像与黑白图像进行比较会导致精度大大降低。 最简单的解决方案是将所有图像转换为黑白图像,这样即使在比较原始彩色图像时也不会降低准确性。
二、仿射变换
观察下图可以发现给我们的图像中存在大量的背景像素,先使用bounding box将鲸鱼的矩形区域框出来,再利用仿射变换将图像的矩形区域映射到分辨率为384x384x1(黑白仅一个通道)的方形图像。矩形区域的宽高比为2.15,接近于所有图片的平均高宽比。矩形的边界框比bounding box模型预测的边界框略大。
在训练期间,通过添加一系列缩放,平移,旋转和剪切的随机变换来进行数据增强。而测试时会跳过这些随机变换。
最后,将图像标准化为零均值和单位方差。
ps:bounding box文件也在上篇博客的百度云资源中,该数据主要是通过网络模型预测得到,具体可参考https://www.kaggle.com/martinpiotte/bounding-box-model
同时在之前的DataSource资源文件夹下添加bounding box的读取
class DataSource():
def __init__(self, TRAIN_DF, SUB_DF, HASH_PATH, SIZE_PATH, BBOX_PATH):
super(DataSource, self).__init__()
# Read the dataset description
self.picture_2_bbox = self.get_bbox(BBOX_PATH) # 每张图片中的bounding box
def get_bbox(self, bbox_path):
# Read the bounding box data from the bounding box
bbox = {p: (x0, y0, x1, y1) for _, p, x0, y0, x1, y1 in pd.read_csv(bbox_path).to_records()}
return bbox
[('72c3ce75c.jpg', (0, 0, 1045, 389)), ('a7ad640ee.jpg', (35, 71, 523, 291)),
('df2b6c364.jpg', (12, 15, 1033, 308)), ('26013fcb5.jpg', (3, 1, 1024, 292)),
('09eff7b37.jpg', (11, 8, 926, 334))]
在util.py在添加包括旋转、裁切、缩放、平移等随机变换的方法
# 图像增强:随机变换
def build_transform(rotation, shear, height_zoom, width_zoom, height_shift, width_shift):
rotation = np.deg2rad(rotation) # 旋转角度转化为弧度
shear = np.deg2rad(shear)
rotation_matrix = np.array(
[[np.cos(rotation), np.sin(rotation), 0], [-np.sin(rotation), np.cos(rotation), 0], [0, 0, 1]]) # 旋转
shear_matrix = np.array([[1, np.sin(shear), 0], [0, np.cos(shear), 0], [0, 0, 1]]) # 裁剪
zoom_matrix = np.array([[1.0 / height_zoom, 0, 0], [0, 1.0 / width_zoom, 0], [0, 0, 1]]) # 放大
shift_matrix = np.array([[1, 0, -height_shift], [0, 1, -width_shift], [0, 0, 1]]) # 位移
return np.dot(np.dot(rotation_matrix, shear_matrix), np.dot(zoom_matrix, shift_matrix))
在DataSouce类下添加读取图片的方法,训练集读取时便进行随机变换进行数据增强,而测试集读取时不必进行变换。
# 读取裁切的图片
def read_cropped_image(self, picture, augment, crop_margin=0.05, anisotropy=2.15, img_shape=(384, 384, 1)):
"""
@param p : the name of the picture to read
@param augment: True/False if data augmentation should be performed
@:param crop_margin:The margin added around the bounding box to compensate for bounding box inaccuracy
@:param anisotropy:高宽比
@return a numpy array with the transformed image
"""
# If an image id was given, convert to filename
if picture in self.hash_2_picture:
picture = self.hash_2_picture[picture]
size_x, size_y = self.picture_2_size[picture]
# Determine the region of the original image we want to capture based on the bounding box.
x0, y0, x1, y1 = self.picture_2_bbox[picture]
# if picture in rotate: #无需旋转的图片
# x0, y0, x1, y1 = size_x - x1, size_y - y1, size_x - x0, size_y - y0
dx = x1 - x0
dy = y1 - y0
x0 -= dx * crop_margin
x1 += dx * crop_margin + 1
y0 -= dy * crop_margin
y1 += dy * crop_margin + 1
# 防止越界
x0 = 0 if x0 < 0 else x0
x1 = size_x if x1 > size_x else x1
y0 = 0 if y0 < 0 else y0
y1 = size_y if y1 > size_y else y1
# 宽高比
dx = x1 - x0
dy = y1 - y0
if dx > dy * anisotropy:
dy = 0.5 * (dx / anisotropy - dy)
y0 -= dy
y1 += dy
else:
dx = 0.5 * (dy * anisotropy - dx)
x0 -= dx
x1 += dx
# Generate the transformation matrix
trans = np.array([[1, 0, -0.5 * img_shape[0]], [0, 1, -0.5 * img_shape[1]], [0, 0, 1]])
trans = np.dot(np.array([[(y1 - y0) / img_shape[0], 0, 0], [0, (x1 - x0) / img_shape[1], 0], [0, 0, 1]]),
trans) # 缩放
if augment: # 数据增强
trans = np.dot(build_transform(
random.uniform(-5, 5),
random.uniform(-5, 5),
random.uniform(0.8, 1.0),
random.uniform(0.8, 1.0),
random.uniform(-0.05 * (y1 - y0), 0.05 * (y1 - y0)),
random.uniform(-0.05 * (x1 - x0), 0.05 * (x1 - x0))
), trans)
trans = np.dot(np.array([[1, 0, 0.5 * (y1 + y0)], [0, 1, 0.5 * (x1 + x0)], [0, 0, 1]]), trans)
# Read the image, transform to black and white and comvert to numpy array
img = np.array(Image.open(expand_path(picture)).convert('L'))
# Apply affine transformation
matrix = trans[:2, :2]
offset = trans[:2, 2]
# img = img.reshape(img.shape[:-1])
img = affine_transform(img, matrix, offset, output_shape=img_shape[:-1], order=1, mode='constant',
cval=np.average(img))
img = img.reshape(img_shape).astype(float)
# Normalize to zero mean and unit variance
img -= np.mean(img, keepdims=True)
img /= np.std(img, keepdims=True)
return img
# Read an image for validation, i.e. without data augmentation.
def read_for_validation(self, img_path, img_size=(384, 384, 1), anisotropy=2.15):
return self.read_cropped_image(img_path, augment=False, img_shape=img_size, anisotropy=anisotropy)
# Read an image for training, i.e. including a random affine transformation
def read_for_training(self, img_path, img_size=(384, 384, 1), anisotropy=2.15):
return self.read_cropped_image(img_path, augment=True, img_shape=img_size, anisotropy=anisotropy)
第一张为原图,第二张为训练时进行随即变换的图片,第三张为测试时不进行随机变换的图片。
原图、训练集图片、测试集图片
总结
以上就是对图片进行预处理的内容,主要是通过bounding box模型将鲸鱼的边界框进行预测,然后将预测出的矩形区域缩小到指定大小,在训练时通过随机变换进行数据增强,而测试时不进行数据增强。
当然边界框的预测也是一项重要内容,它直接关系到鲸鱼边界框的准确性,继而影响训练图像的质量,关于预测边界框的网络模型会另开一篇博客进行讲解。