(注释在代码中
数据集预处理
数据预处理是训练神经网络时非常重要的步骤。合适的预处理方法,可以帮助模型更好的收敛并防止过拟合。然后需要对这些数据进行预处理,为了保证网络运行的速度,通常还要对数据预处理进行加速。
首先我们需要从磁盘读入数据,前面已经将图片的所有描述信息保存在records中了,其中每一个元素都包含了一张图片的描述,下面的程序展示了如何根据records里面的描述读取图片及标注。
# 数据读取
import cv2
#用于处理真实框的坐标和类别标签的函数将真实框的坐标和类别标签限制在指定的数量内,并将不足数量的部分置为零。这样可以确保在后续的目标检测任务中,
#每张图片上最多只有50个真实框,并且所有的真实框坐标和类别标签都存储在固定大小的数组中,方便后续处理。
def get_bbox(gt_bbox, gt_class): #gt_bbox是一个包含真实框坐标的二维数组,gt_class是一个包含真实框类别标签的一维数组。
# 对于一般的检测任务来说,一张图片上往往会有多个目标物体
# 设置参数MAX_NUM = 50, 即一张图片最多取50个真实框;如果真实
# 框的数目少于50个,则将不足部分的gt_bbox, gt_class和gt_score的各项数值全设置为0
MAX_NUM = 50
gt_bbox2 = np.zeros((MAX_NUM, 4)) #创建了一个大小为(MAX_NUM, 4)的全零二维数组gt_bbox2,用于存储处理后的真实框坐标
gt_class2 = np.zeros((MAX_NUM,)) #用于存储处理后的真实框类别标签
for i in range(len(gt_bbox)): #将原始的真实框坐标和类别标签复制到gt_bbox2和gt_class2中。循环从0到len(gt_bbox)进行迭代
#,每次将第i个真实框的坐标赋值给gt_bbox2[i, :],将第i个真实框的类别标签赋值给gt_class2[i]。如果i超过了MAX_NUM,则跳出循环,不再处理剩余的真实框。
gt_bbox2[i, :] = gt_bbox[i, :]
gt_class2[i] = gt_class[i]
if i >= MAX_NUM:
break
return gt_bbox2, gt_class2
def get_img_data_from_file(record):
"""
record is a dict as following,
record = {
'im_file': img_file,
'im_id': im_id, #图像的唯一标识符
'h': im_h,
'w': im_w,
'is_crowd': is_crowd,
'gt_class': gt_class,
'gt_bbox': gt_bbox,
'gt_poly': [],
'difficult': difficult
}
"""
im_file = record['im_file'] #图像文件路径
h = record['h']
w = record['w']
is_crowd = record['is_crowd'] #表示目标是否是一群物体。
gt_class = record['gt_class'] #包含真实框的类别标签
gt_bbox = record['gt_bbox'] #包含真实框的坐标。
difficult = record['difficult']
img = cv2.imread(im_file)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# check if h and w in record equals that read from img
assert img.shape[0] == int(h), \
"image height of {} inconsistent in record({}) and img file({})".format(
im_file, h, img.shape[0])
assert img.shape[1] == int(w), \
"image width of {} inconsistent in record({}) and img file({})".format(
im_file, w, img.shape[1])
gt_boxes, gt_labels = get_bbox(gt_bbox, gt_class)
# gt_bbox 用相对值。处理真实框的坐标和类别标签。get_bbox函数会将真实框的坐标和类别标签复制到新的数组中
#,并根据参数MAX_NUM的值将其限制在最大数量范围内(最多50个真实框)。如果真实框的数量超过了最大数量,多余的部分会被忽略。函数还会将真实框的坐标转换为相对于图像宽度和高度的比例。
gt_boxes[:, 0] = gt_boxes[:, 0] / float(w)
gt_boxes[:, 1] = gt_boxes[:, 1] / float(h)
gt_boxes[:, 2] = gt_boxes[:, 2] / float(w)
gt_boxes[:, 3] = gt_boxes[:, 3] / float(h)
#返回读取的图像、处理后的真实框的坐标、真实框的类别标签以及图像的高度和宽度。这些信息可以用于后续的目标检测任务。
return img, gt_boxes, gt_labels, (h, w)
用于从文件中读取图像数据的函数。它使用OpenCV库来读取图像文件,并将图像转换为RGB格式。然后,它会检查读取的图像的高度和宽度是否与记录中的值一致。接下来,它会调用get_bbox
函数来处理真实框的坐标和类别标签。最后,它会将真实框的坐标转换为相对于图像宽度和高度的比例。该函数返回读取的图像,真实框的坐标,真实框的类别标签以及图像的高度和宽度。
record = records[0]
#get_img_data_from_file 接受一个记录作为输入,返回包含图像、真实框、真实标签和缩放比例的元组
#img.shape 返回图像的尺寸,gt_labels 返回真实标签,scales 返回缩放比例。这些信息可以用于后续的模型训练和评估。
img, gt_boxes, gt_labels, scales = get_img_data_from_file(record)
print('img shape:{}, \n gt_labels:{}, \n scales:{}\n'.format(img.shape, gt_labels, scales))
读取记录列表中的第一个记录,并从中获取图像、真实框、真实标签和缩放比例,然后打印出它们的信息。
数据增强
import numpy as np
import cv2
from PIL import Image, ImageEnhance
import random
# 随机改变亮暗、对比度和颜色等
def random_distort(img):
# 随机改变亮度
def random_brightness(img, lower=0.5, upper=1.5):
e = np.random.uniform(lower, upper)
return ImageEnhance.Brightness(img).enhance(e)
# 随机改变对比度
def random_contrast(img, lower=0.5, upper=1.5):
e = np.random.uniform(lower, upper)
return ImageEnhance.Contrast(img).enhance(e)
# 随机改变颜色
def random_color(img, lower=0.5, upper=1.5):
e = np.random.uniform(lower, upper)
return ImageEnhance.Color(img).enhance(e)
ops = [random_brightness, random_contrast, random_color]
np.random.shuffle(ops) #对操作列表进行随机打乱,保证每次调用random_distort函数时,图像增强的顺序都是随机的。
img = Image.fromarray(img) #将图像转换为PIL.Image类型
img = ops[0](img) #依次i增强
img = ops[1](img)
img = ops[2](img)
img = np.asarray(img)
return img
# 定义可视化函数,用于对比原图和图像增强的效果
import matplotlib.pyplot as plt
%matplotlib inline
def visualize(srcimg, img_enhance): #绘制两个子图,分别显示原图和增强后的图像
# 图像可视化
plt.figure(num=2, figsize=(6,12)) #创建图片对象
plt.subplot(1,2,1) #在图片上创建两个子图,分别显示原图和增强后的图像
plt.title('Src Image', color='#0000FF') #设置子图的标题和颜色
plt.axis('off') # 不显示坐标轴
plt.imshow(srcimg) # 显示原图片
# 对原图做 随机改变亮暗、对比度和颜色等 数据增强
srcimg_gtbox = records[0]['gt_bbox']
srcimg_label = records[0]['gt_class']
plt.subplot(1,2,2)
plt.title('Enhance Image', color='#0000FF')
plt.axis('off') # 不显示坐标轴
plt.imshow(img_enhance)
image_path = records[0]['im_file'] #读取了记录列表中的第一个记录的图像路径,并使用PIL库打开图像
print("read image from file {}".format(image_path))
srcimg = Image.open(image_path)
# 将PIL读取的图像转换成array类型
srcimg = np.array(srcimg)
# 对原图做 随机改变亮暗、对比度和颜色等 数据增强
img_enhance = random_distort(srcimg)
visualize(srcimg, img_enhance)
这段代码实现了图像的随机增强功能,并提供了可视化函数用于对比原图和增强后的图像效果。可以通过随机增强操作来扩充训练数据集,提高模型的泛化能力。
随机填充
# 随机填充 接受输入图像img和ground truth boxesgtboxes,以及一些可选参数
def random_expand(img,
gtboxes,
max_ratio=4.,
fill=None,
keep_ratio=True,
thresh=0.5):
#比较随机数和阈值thresh来决定是否进行填充操作。如果随机数大于阈值,函数将直接返回原图像和原始ground truth boxes,不进行填充。
if random.random() > thresh:
return img, gtboxes
#检查最大填充比例max_ratio是否小于1.0。如果是,则也直接返回原图像和原始ground truth boxes,不进行填充。
if max_ratio < 1.0:
return img, gtboxes
#获取原图像的高度h、宽度w和通道数c。生成填充比例ratio_x和ratio_y
h, w, c = img.shape
ratio_x = random.uniform(1, max_ratio)
#ratio_x在[1, max_ratio]范围内随机取值,而ratio_y则根据keep_ratio参数决定是与ratio_x
#相等还是在[1, max_ratio]范围内随机取值。计算填充后的图像高度oh和宽度ow。
if keep_ratio:
ratio_y = ratio_x
else:
ratio_y = random.uniform(1, max_ratio)
oh = int(h * ratio_y)
ow = int(w * ratio_x)
#随机生成填充后图像的左上角坐标off_x和off_y,取值范围分别为[0, ow-w]和[0, oh-h]。
off_x = random.randint(0, ow - w)
off_y = random.randint(0, oh - h)
#创建一个与填充后图像尺寸相同的全零数组out_img,其形状为(oh, ow, c)。如果提供了填充颜色fill且颜色通道数与图像通道数相同,
#则将颜色值乘以255,并将结果赋值给out_img。
out_img = np.zeros((oh, ow, c))
if fill and len(fill) == c:
for i in range(c):
out_img[:, :, i] = fill[i] * 255.0
#将原图像复制到out_img指定位置,进行填充操作。
out_img[off_y:off_y + h, off_x:off_x + w, :] = img
#更新ground truth boxes的坐标和大小,将其调整到填充后图像的尺寸上。
gtboxes[:, 0] = ((gtboxes[:, 0] * w) + off_x) / float(ow)
gtboxes[:, 1] = ((gtboxes[:, 1] * h) + off_y) / float(oh)
gtboxes[:, 2] = gtboxes[:, 2] / ratio_x
gtboxes[:, 3] = gtboxes[:, 3] / ratio_y
#将增强后的图像转换为uint8类型,并返回增强后的图像和更新后的ground truth boxes。
return out_img.astype('uint8'), gtboxes
srcimg_gtbox = records[0]['gt_bbox'] #records是数据集的一个列表,每个元素包含图像和ground truth boxes等信息。srcimg_gtbox是第一张图像的ground truth boxes。
srcimg_label = records[0]['gt_class'] #srcimg_label是第一张图像的ground truth labels。
img_enhance, new_gtbox = random_expand(srcimg, srcimg_gtbox) #random_expand函数对第一张图像进行随机填充增强,并返回增强后的图像和更新后的ground truth boxes。
visualize(srcimg, img_enhance) #visualize函数用于可视化比较原始图像和增强后的图像。
这段代码实现了对输入图像进行随机填充增强的功能。随机填充可以增加训练数据集的多样性,提高模型的泛化能力。
随机反转
# 随机翻转
def random_flip(img, gtboxes, thresh=0.5):
#比较随机数与阈值thresh的大小来决定是否进行翻转操作。如果随机数大于阈值,
#则对图像进行水平翻转(即沿y轴翻转),同时更新ground truth boxes中的x坐标值。最后返回翻转后的图像和更新后的ground truth boxes。
if random.random() > thresh:
img = img[:, ::-1, :]
gtboxes[:, 0] = 1.0 - gtboxes[:, 0]
return img, gtboxes
# 对原图做 随机改变亮暗、对比度和颜色等 数据增强
#对原始图像进行了随机翻转操作,并将增强后的图像和更新后的ground truth boxes
#保存为img_enhance和box_enhance。最后利用visualize函数对原始图像和增强后的图像进行可视化比较。
img_enhance, box_enhance = random_flip(srcimg, srcimg_gtbox)
visualize(srcimg, img_enhance)
这段代码实现了随机翻转数据增强功能,并对原始图像进行了随机翻转操作。
小结
在目标检测任务中,数据增强是一种常用的技术,可以通过对原始图像进行一系列变换和扩充来增加训练数据的多样性,提高模型的鲁棒性和泛化能力。下面将介绍在叶病虫害检测中的两种常用数据增强方法:随机填充和随机反转。
-
随机填充(Random Padding): 随机填充是指在图像的周围随机添加一定数量的像素,以扩大图像的尺寸。这个过程可以通过以下步骤实现:
- 随机生成填充的宽度和高度值。
- 将原始图像放置在新的空白画布上,并根据生成的填充值调整原始图像的位置。
- 对超出原始图像边界的区域进行处理,可以选择将其设置为背景或者进行其他处理方式。
- 更新目标框的位置信息,使其适应填充后的图像。
通过随机填充,可以增加训练样本的多样性,使模型对于不同大小的目标和不同场景的适应能力更强。
-
随机反转(Random Flip): 随机反转是指在水平方向上对图像进行翻转操作,即左右翻转。这个过程可以通过以下步骤实现:
- 生成一个随机数。
- 如果随机数大于设定的阈值,则对图像进行水平翻转。
- 更新目标框的位置信息,使其适应翻转后的图像。
随机反转可以增加训练样本的多样性,提供更多不同角度和方向的目标样本,从而提升模型的鲁棒性。
在叶病虫害检测任务中,通过随机填充和随机反转等数据增强方法,可以提高模型对于不同尺寸、角度和方向的叶病虫害目标的检测能力,从而提升整体的检测性能和准确率。