数据集
使用 pascal-voc-trainval-2007 数据集
导包
import os,glob
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt
from matplotlib import patches
from PIL import Image
import IPython.display as display
from lxml import etree
import xml.etree.ElementTree as ET
print(tf.__version__)
print(tf.test.is_gpu_available())
定义常量
obj_names = ("aeroplane","bicycle","bird","boat","bottle","bus","car","cat","chair","cow","diningtable","dog","horse","motorbike","person","pottedplant","sheep","sofa","train","tvmonitor","head","hand","foot")
img_path='/kaggle/input/pascal-voc-trainval-2007/VOCdevkit/VOC2007/JPEGImages'
# img_val_path='/kaggle/input/grass-sugarbeet/data/val/image'
ann_path='/kaggle/input/pascal-voc-trainval-2007/VOCdevkit/VOC2007/Annotations'
yolo_weight_path='/kaggle/input/yoloweights/yolo.weights'
IMGSZ = 416
GRIDSZ = 13
batch=8 #如果内存不够就减小
"""
预设ANCHORS 宽高,第一个为宽度、第二个为高度
"""
ANCHORS = np.array(((0.57273, 0.677385), (1.87446, 2.06253), (3.33843, 5.47434), (7.88282, 3.52778), (9.77052, 9.16828)))
实现函数
获取所有图片路径,与图片对应的边框坐标
由于边框数量不一致,所以边框的矩阵要以最多的为主
DEBUG=False
def parse_annotation(img_dir, ann_dir, labels):
# img_dir: image path
# ann_dir: annotation xml file path
# labels: ('sugarweet', 'weed')
# parse annotation info from xml file
"""
<annotation>
<object>
<name>sugarbeet</name>
<bndbox>
<xmin>1</xmin>
<ymin>250</ymin>
<xmax>53</xmax>
<ymax>289</ymax>
</bndbox>
</object>
<object>....
"""
imgs_info = []
max_boxes = 0
# for each annotation xml file
for ann in os.listdir(ann_dir):
tree = ET.parse(os.path.join(ann_dir, ann))
img_info = dict()
boxes_counter = 0
img_info['object'] = []
for elem in tree.iter():
if 'filename' in elem.tag:
img_info['filename'] = os.path.join(img_dir,elem.text)
if 'width' in elem.tag:
img_info['width'] = int(elem.text)
# assert img_info['width'] == 512
if 'height' in elem.tag:
img_info['height'] = int(elem.text)
# assert img_info['height'] == 512
if 'object' in elem.tag or 'part' in elem.tag:
# x1-y1-x2-y2-label
object_info = [0,0,0,0,0]
boxes_counter += 1
for attr in list(elem):
if 'name' in attr.tag:
try:
label = labels.index(attr.text) + 1
except Exception as e:
print("error:",attr.text,img_info['filename'])
raise e
object_info[4] = label
# if DEBUG:
# print("label::",label)
if 'bndbox' in attr.tag:
for pos in list(attr):
if 'xmin' in pos.tag:
object_info[0] = int(float(pos.text))
if 'ymin' in pos.tag:
object_info[1] = int(float(pos.text))
if 'xmax' in pos.tag:
object_info[2] = int(float(pos.text))
if 'ymax' in pos.tag:
object_info[3] = int(float(pos.text))
img_info['object'].append(object_info)
imgs_info.append(img_info) # filename, w/h/box_info
# (N,5)=(max_objects_num, 5)
if boxes_counter > max_boxes:
max_boxes = boxes_counter
# the maximum boxes number is max_boxes
# [b, 40, 5]
boxes = np.zeros([len(imgs_info), max_boxes, 5])
# print(boxes.shape)
imgs = [] # filename list
imgs_size=[]
for i, img_info in enumerate(imgs_info):
# [N,5]
img_boxes = np.array(img_info['object'])
# overwrite the N boxes info
boxes[i,:img_boxes.shape[0]] = img_boxes
imgs.append(img_info['filename'])
imgs_size.append([img_info['width'],img_info['height']])
# print(img_info['filename'], boxes[i,:5])
# imgs: list of image path
# boxes: [b,40,5]
return imgs, tf.constant(boxes),tf.constant(imgs_size)
调用
imgs, boxes,imgs_size = parse_annotation(img_path, ann_path, obj_names)
图片总数
img_size=len(imgs)
img_size
查看返回结果信息
boxes.shape,imgs_size.shape
测试、查看
实现函数,显示图片,并框出目标,测试数据读取是否正确
from matplotlib import pyplot as plt
from matplotlib import patches
from PIL import Image
def show_by_path(img, img_boxes):
# imgs:[b, 512, 512, 3]
# imgs_boxes: [b, 40, 5]
f,ax1 = plt.subplots(1,figsize=(10,10))
# display the image, [512,512,3]
ax1.imshow(Image.open(img))
for x1,y1,x2,y2,l in img_boxes: # [40,5]
x1,y1,x2,y2 = float(x1), float(y1), float(x2), float(y2)
w = x2 - x1
h = y2 - y1
"""
if l==1: # green for sugarweet
color = (0,1,0)
elif l==2: # red for weed
color = (1,0,0) # (R,G,B)
else: # ignore invalid boxes
break
"""
if l>=1 and l<=len(obj_names): # green for sugarweet
color = (0,1,0)
else: # ignore invalid boxes
break
rect = patches.Rectangle((x1,y1), w, h, linewidth=2,
edgecolor=color, facecolor='none')
ax1.add_patch(rect)
show_by_path(imgs[1], boxes[1])
show_by_path(imgs[2], boxes[2])
show_by_path(imgs[30], boxes[30])
show_by_path(imgs[300], boxes[300])
show_by_path(imgs[4000], boxes[4000])
调整边框坐标
DEBUG=True
def modify_coordinate(boxes,imgs_size):
# if DEBUG:
# print(boxes.shape,imgs_size.shape)
w=imgs_size[:,0]
h=imgs_size[:,1]
w=tf.reshape(w,[w.shape[0],1])
w=tf.cast(w,tf.float64)
h=tf.reshape(h,[h.shape[0],1])
h=tf.cast(h,tf.float64)
# if DEBUG:
# print(w,h)
xmin=boxes[:,:,0]
ymin=boxes[:,:,1]
xmax=boxes[:,:,2]
ymax=boxes[:,:,3]
typ=boxes[:,:,4]
# if DEBUG:
# print("xmin.shape::",xmin.shape)
# # print(xmin)
# print(ymin.shape)
# print(xmax.shape)
# print(ymax.shape)
# print("typ.shape:",typ.shape)
# pos_info=boxes[index]
# w,h=imgs_size[index]
# # plt.imshow(img.numpy())
# # print(w,h)
# box_xmin=tf.divide(xmin*IMGSZ,tf.cast(w,tf.float64))
box_xmin=xmin*IMGSZ/w
box_ymin=ymin*IMGSZ/h
box_xmax=xmax*IMGSZ/w
box_ymax=ymax*IMGSZ/h
# if DEBUG:
# print("box_xmin::",box_xmin)
res=tf.stack([box_xmin,box_ymin,box_xmax,box_ymax,typ],axis=1)
res=tf.transpose(res,perm=[0,2,1])
res=tf.cast(res,tf.int32)
return res
boxes_resize=modify_coordinate(boxes,imgs_size)
# print(boxes_resize.shape)
def preprocess(img, img_boxes):
# img: string
# img_boxes: [40,5]
x = tf.io.read_file(img)
x = tf.image.decode_png(x, channels=3)
x = tf.image.resize(x,(IMGSZ,IMGSZ))
x = tf.image.convert_image_dtype(x, tf.float32)
return x, img_boxes
def get_dataset(img_dir, ann_dir):
# return tf dataset
# [b], boxes [b, 40, 5]
# imgs, boxes,imgs_size = parse_annotation(img_dir, ann_dir, obj_names)
# boxes_resize=modify_coordinate(boxes,imgs_size)
db = tf.data.Dataset.from_tensor_slices((imgs, boxes_resize))
db = db.map(preprocess).shuffle(img_size).batch(batch)
print('db Images:', len(imgs))
return db
train_db = get_dataset(img_path, ann_path)
print(train_db)
显示db中的图片和边框
def show_by_mat(img, img_boxes):
"""
img:图像矩阵
img_boxes:原始盒子(左上、右下坐标系)
"""
# print(img.shape)
# imgs:[b, 512, 512, 3]
# imgs_boxes: [b, 40, 5]
f,ax1 = plt.subplots(1,figsize=(10,10))
# display the image, [512,512,3]
ax1.imshow(img)
# ax1.imshow(Image.open(img))
for x1,y1,x2,y2,l in img_boxes: # [40,5]
x1,y1,x2,y2 = float(x1), float(y1), float(x2), float(y2)
w = x2 - x1
h = y2 - y1
"""
if l==1: # green for sugarweet
color = (0,1,0)
elif l==2: # red for weed
color = (1,0,0) # (R,G,B)
else: # ignore invalid boxes
break
"""
if l>=1 and l<=len(obj_names): # green for sugarweet
color = (0,1,0)
else: # ignore invalid boxes
break
rect = patches.Rectangle((x1,y1), w, h, linewidth=2,
edgecolor=color, facecolor='none')
ax1.add_patch(rect)
def process_true_boxes(gt_boxes):
# gt_boxes: [40,5]
# 512//16=32
scale = IMGSZ // GRIDSZ
# [5,2]
# mask for object
detector_mask = np.zeros([GRIDSZ, GRIDSZ, 5, 1])
#x-y-w-h-l
matching_gt_box = np.zeros([GRIDSZ, GRIDSZ, 5, 5])
# [40,5] x1-y1-x2-y2-l => x-y-w-h-l
gt_boxes_grid = np.zeros(gt_boxes.shape)
# DB: tensor => numpy
gt_boxes = gt_boxes.numpy()
for i in range(gt_boxes.shape[0]): # [40,5]
# box: [5], x1-y1-x2-y2-l
# 512 => 16
box=gt_boxes[i]
x = ((box[0]+box[2])/2)/scale
y = ((box[1]+box[3])/2)/scale
w = (box[2] - box[0]) / scale
h = (box[3] - box[1]) / scale
# [40,5] x-y-w-h-l
gt_boxes_grid[i] = [x,y,w,h,box[4]]
if w*h > 0: # valid box
# x,y: 7.3, 6.8
best_anchor = 0
best_iou = 0
for j in range(5):
interct = np.minimum(w, ANCHORS[j,0]) * np.minimum(h, ANCHORS[j,1])
union = w*h + (ANCHORS[j,0]*ANCHORS[j,1]) - interct
iou = interct / union
if iou > best_iou: # best iou
best_anchor = j
best_iou = iou
# found the best anchors
if best_iou>0:
x_coord = np.floor(x).astype(np.int32)
y_coord = np.floor(y).astype(np.int32)
# [b,h,w,5,1]
detector_mask[y_coord, x_coord, best_anchor] = 1
# [b,h,w,5,x-y-w-h-l]
matching_gt_box[y_coord, x_coord, best_anchor] = np.array([x,y,w,h,box[4]])
# [40,5] => [16,16,5,5]
# [16,16,5,5]
# [16,16,5,1]
# [40,5]
return matching_gt_box, detector_mask, gt_boxes_grid
def show_by_mat_center(img, gt_boxes_grid):
"""
img:图像矩阵
boxes:盒子,中心点(x,y),宽度、高度、分类
"""
# imgs:[b, 512, 512, 3]
# imgs_boxes: [b, 40, 5]
img=tf.cast(img,tf.int32)
f,ax1 = plt.subplots(1,figsize=(10,10))
# display the image, [512,512,3]
ax1.imshow(img)
scale = IMGSZ // GRIDSZ
for x,y,w,h,l in gt_boxes_grid: # [40,5]
x,y,w,h = float(x)*scale, float(y)*scale, float(w)*scale, float(h)*scale
x=x-w/2
y=y-h/2
if l>=1 and l<=len(obj_names): # green for sugarweet
color = (0,1,0)
else: # ignore invalid boxes
break
rect = patches.Rectangle((x,y), w, h, linewidth=2,
edgecolor=color, facecolor='none')
ax1.add_patch(rect)
def ground_truth_generator(db):
for imgs, imgs_boxes in db:
# imgs: [b,512,512,3]
# imgs_boxes: [b,40,5]
batch_matching_gt_box = []
batch_detector_mask = []
batch_gt_boxes_grid = []
# print(imgs_boxes[0,:5])
# print("imgs_boxes.shape::",imgs_boxes.shape)
for i in range(imgs_boxes.shape[0]): # for each image
# print("imgs_boxes[i].shape::",imgs_boxes[i].shape)
matching_gt_box, detector_mask, gt_boxes_grid = process_true_boxes(imgs_boxes[i])
batch_matching_gt_box.append(matching_gt_box)
batch_detector_mask.append(detector_mask)
batch_gt_boxes_grid.append(gt_boxes_grid)
# [b, 16,16,5,1]
detector_mask = tf.cast(np.array(batch_detector_mask), dtype=tf.float32)
# [b,16,16,5,5] x-y-w-h-l
matching_gt_box = tf.cast(np.array(batch_matching_gt_box), dtype=tf.float32)
# [b,40,5] x-y-w-h-l
gt_boxes_grid = tf.cast(np.array(batch_gt_boxes_grid), dtype=tf.float32)
# [b,16,16,5]
matching_classes = tf.cast(matching_gt_box[...,4], dtype=tf.int32)
# [b,16,16,5,3]
matching_classes_oh = tf.one_hot(matching_classes, depth=3)
# x-y-w-h-conf-l1-l2
# [b,16,16,5,2]
matching_classes_oh = tf.cast(matching_classes_oh[...,1:], dtype=tf.float32)
# [b,512,512,3]
# [b,16,16,5,1]
# [b,16,16,5,5]
# [b,16,16,5,2]
# [b,40,5]
yield imgs, detector_mask, matching_gt_box, matching_classes_oh,gt_boxes_grid