处理之后的图像句柄传到显示框_深度学习目标检测与预处理模块
一般的深度學習的部署流程
對計算機而言,能夠“看到”的是圖像被編碼之后的數字,但它很難解高層語義概念,比如圖像或者視頻幀中出現目標的是人還是物體,更無法定位目標出現在圖像中哪個區域。目標檢測的主要目的是讓計算機可以自動識別圖片或者視頻幀中所有目標的類別,并在該目標周圍繪制邊界框,標示出每個目標的位置,如?圖1?所示。?
圖1:圖像分類和目標檢測示意圖
圖1(a)是圖像分類任務,只需識別出這是一張斑馬的圖片(但需要先選出候選區域)。
圖1(b)是目標檢測任務,不僅要識別出這是一張斑馬的圖片,還要標出圖中斑馬的位置。
目標檢測發展歷程
圖像分類處理基本流程,先使用卷積神經網絡提取圖像特征,然后再用這些特征預測分類概率,根據訓練樣本標簽建立起分類損失函數,開啟端到端的訓練,如?圖2?所示。?
圖2:圖像分類流程示意圖
但對于目標檢測問題,按照?圖2?的流程則行不通。因為在圖像分類任務中,對整張圖提取特征的過程中沒能體現出不同目標之間的區別,最終也就沒法分別標示出每個物體所在的位置。
為了解決這個問題,結合圖片分類任務取得的成功經驗,我們可以將目標檢測任務進行拆分。假設我們現在有某種方式可以在輸入圖片上生成一系列可能包含物體的區域,這些區域稱為候選區域,在一張圖上可以生成很多個候選區域。然后對每個候選區域,可以把它單獨當成一幅圖像來看待,使用圖像分類模型對它進行分類,看它屬于哪個類別或者背景(即不包含任何物體的類別)。
解決圖像分類任務,使用卷積神經網絡對一幅圖像進行分類不再是一件困難的事情。那么,現在問題的關鍵就是如何產生候選區域?比如我們可以使用窮舉法來產生候選區域,如圖3所示。?
圖3:候選區域
A為圖像上的某個像素點,B為A右下方另外一個像素點,A、B兩點可以確定一個矩形框,記作AB。
如圖3(a)所示:A在圖片左上角位置,B遍歷除A之外的所有位置,生成矩形框A1B1, …, A1Bn, …
如圖3(b)所示:A在圖片中間某個位置,B遍歷A右下方所有位置,生成矩形框AkB1, …, AkBn, …
當A遍歷圖像上所有像素點,B則遍歷它右下方所有的像素點,最終生成的矩形框集合{AiBj}將會包含圖像上所有可以選擇的區域。
只要我們對每個候選區域的分類足夠的準確,則一定能找到跟實際物體足夠接近的區域來。窮舉法也許能得到正確的預測結果,但其計算量也是非常巨大的,在100*100圖像尺寸下其所生成的總的候選區域數目,總數將會達到2.5×10^7?7個,如此多的候選區域使得這種方法幾乎沒有什么實用性。但是通過這種方式,我們可以看出,假設分類任務完成的足夠完美,從理論上來講檢測任務也是可以解決的,亟待解決的問題是如何設計出合適的方法來產生候選區域。
科學家們開始思考,是否可以應用傳統圖像算法先產生候選區域,然后再用卷積神經網絡對這些區域進行分類?
2013年,Ross Girshick 等人于首次將CNN的方法應用在目標檢測任務上,他們使用傳統圖像算法selective search產生候選區域,取得了極大的成功,這就是對目標檢測領域影響深遠的區域卷積神經網絡(R-CNN)模型。
2015年,Ross Girshick 對此方法進行了改進,提出了Fast RCNN模型。通過將不同區域的物體共用卷積層的計算,大大縮減了計算量,提高了處理速度,而且還引入了調整目標物體位置的回歸方法,進一步提高了位置預測的準確性。
2015年,Shaoqing Ren 等人提出了Faster RCNN模型,提出了RPN的方法來產生物體的候選區域,這一方法里面不再需要使用傳統的圖像處理算法來產生候選區域,進一步提升了處理速度。
2017年,Kaiming He 等人于提出了Mask RCNN模型,只需要在Faster RCNN模型上添加比較少的計算量,就可以同時實現目標檢測和物體實例分割兩個任務。
以上都是基于R-CNN系列的著名模型,對目標檢測方向的發展有著較大的影響力。此外,還有一些其他模型,比如SSD、YOLO(1, 2, 3)、R-FCN等也都是目標檢測領域流行的模型結構。
R-CNN的系列算法分成兩個階段,先在圖像上產生候選區域,再對候選區域進行分類并預測目標物體位置,它們通常被叫做兩階段檢測算法。SSD和YOLO算法則只使用一個網絡同時產生候選區域并預測出物體的類別和位置,所以它們通常被叫做單階段檢測算法。
圖像檢測基礎概念:介紹與目標檢測任相關的基本概念,包括邊界框、錨框和交并比等。
目標檢測基礎概念
在介紹目標檢測算法之前,先介紹一些跟檢測相關的基本概念,包括邊界框、錨框和交并比等。
邊界框(bounding box)
檢測任務需要同時預測物體的類別和位置,因此需要引入一些跟位置相關的概念。通常使用邊界框(bounding box,bbox)來表示物體的位置,邊界框是正好能包含住物體的矩形框,如?圖4?所示,圖中人臉對應的邊界框。?
圖4:邊界框通常有兩種格式來表示邊界框的位置:
,這樣的邊界框也被稱為真實框(ground truth box),如?圖4?所示,圖中畫出了人像所對應的真實框。模型會對目標物體可能出現的位置進行預測,由模型預測出的邊界框則稱為預測框(prediction box)。
注意:
不同的代碼使用的可能是不同方式。
圖片坐標的原點在左上角,x軸向右為正方向,y軸向下為正方向。
要完成一項檢測任務,我們通常希望模型能夠根據輸入的圖片,輸出一些預測的邊界框,以及邊界框中所包含的物體的類別或者說屬于某個類別的概率,例如【L ,P x1,y1,x2,y2】
這其中L是類別標簽,P是物體屬于該類別的概率。一張輸入圖片可能會產生多個預測框,接下來讓我們一起學習如何完成這樣一項任務。
錨框(Anchor)
錨框與物體邊界框不同,是由人們假想出來的一種框。先設定好錨框的大小和形狀,再以圖像上某一個點為中心畫出矩形框。,以像素點[300, 500]為中心可以使用下面的程序生成3個框,其中錨框A1跟目標區域非常接近。
# 畫圖展示如何繪制邊界框和錨框import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.image import imread
import math
# 定義畫矩形框的程序
def draw_rectangle(currentAxis, bbox, edgecolor = 'k', facecolor = 'y', fill=False, linestyle='-'):
# currentAxis,坐標軸,通過plt.gca()獲取
# bbox,邊界框,包含四個數值的list, [x1, y1, x2, y2]
# edgecolor,邊框線條顏色
# facecolor,填充顏色
# fill, 是否填充
# linestype,邊框線型
# patches.Rectangle需要傳入左上角坐標、矩形區域的寬度、高度等參數
rect=patches.Rectangle((bbox[0], bbox[1]), bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, linewidth=1,
edgecolor=edgecolor,facecolor=facecolor,fill=fill, linestyle=linestyle)
currentAxis.add_patch(rect)
plt.figure(figsize=(10, 10))
filename = '/home/aistudio/work/images/section3/000000086956.jpg'
im = imread(filename)
plt.imshow(im)
# 使用xyxy格式表示物體真實框
bbox1 = [214.29, 325.03, 399.82, 631.37]
bbox2 = [40.93, 141.1, 226.99, 515.73]
bbox3 = [247.2, 131.62, 480.0, 639.32]
currentAxis=plt.gca()
draw_rectangle(currentAxis, bbox1, edgecolor='r')
draw_rectangle(currentAxis, bbox2, edgecolor='r')
draw_rectangle(currentAxis, bbox3,edgecolor='r')
# 繪制錨框
def draw_anchor_box(center, length, scales, ratios, img_height, img_width):
"""
以center為中心,產生一系列錨框
其中length指定了一個基準的長度
scales是包含多種尺寸比例的list
ratios是包含多種長寬比的list
img_height和img_width是圖片的尺寸,生成的錨框范圍不能超出圖片尺寸之外
"""
bboxes = []
for scale in scales:
for ratio in ratios:
h = length*scale*math.sqrt(ratio)
w = length*scale/math.sqrt(ratio)
x1 = max(center[0] - w/2., 0.)
y1 = max(center[1] - h/2., 0.)
x2 = min(center[0] + w/2. - 1.0, img_width - 1.0)
y2 = min(center[1] + h/2. - 1.0, img_height - 1.0)
print(center[0], center[1], w, h)
bboxes.append([x1, y1, x2, y2])
for bbox in bboxes:
draw_rectangle(currentAxis, bbox, edgecolor = 'b')
img_height = im.shape[0]
img_width = im.shape[1]
draw_anchor_box([300., 500.], 100., [2.0], [0.5, 1.0, 2.0], img_height, img_width)
################# 以下為添加文字說明和箭頭###############################
plt.text(285, 285, 'G1', color='red', fontsize=20)
plt.arrow(300, 288, 30, 40, color='red', width=0.001, length_includes_head=True, \
head_width=5, head_length=10, shape='full')
plt.text(190, 320, 'A1', color='blue', fontsize=20)
plt.arrow(200, 320, 30, 40, color='blue', width=0.001, length_includes_head=True, \
head_width=5, head_length=10, shape='full')
plt.text(160, 370, 'A2', color='blue', fontsize=20)
plt.arrow(170, 370, 30, 40, color='blue', width=0.001, length_includes_head=True, \
head_width=5, head_length=10, shape='full')
plt.text(115, 420, 'A3', color='blue', fontsize=20)
plt.arrow(127, 420, 30, 40, color='blue', width=0.001, length_includes_head=True, \
head_width=5, head_length=10, shape='full')
#draw_anchor_box([200., 200.], 100., [2.0], [0.5, 1.0, 2.0])
plt.show()
300.0 500.0 282.84271247461896 141.4213562373095
300.0 500.0 200.0 200.0
300.0 500.0 141.42135623730948 282.842712474619
在目標檢測模型中,通常會以某種規則在圖片上生成一系列錨框,將這些錨框當成可能的候選區域。模型對這些候選區域是否包含物體進行預測,如果包含目標物體,則還需要進一步預測出物體所屬的類別。還有更為重要的一點是,由于錨框位置是固定的,它不大可能剛好跟物體邊界框重合,所以需要在錨框的基礎上進行微調以形成能準確描述物體位置的預測框,模型需要預測出微調的幅度。在訓練過程中,模型通過學習不斷的調整參數,最終能學會如何判別出錨框所代表的候選區域是否包含物體,如果包含物體的話,物體屬于哪個類別,以及物體邊界框相對于錨框位置需要調整的幅度,不同的模型往往有著不同的生成錨框的方式.
交并比
上面我們畫出了以點(300,500)(300, 500)(300,500)為中心,生成的三個錨框,我們可以看到錨框A1 與真實框 G1的重合度比較好。那么如何衡量這三個錨框跟真實框之間的關系呢,在檢測任務中是使用交并比(Intersection of Union,IoU)作為衡量指標。這一概念來源于數學中的集合,用來描述兩個集合AAA和BBB之間的關系,它等于兩個集合的交集里面所包含的元素個數,除以它們的并集里面所包含的元素個數,具體計算公式如下:
IoU=A∩BA∪BIoU = \frac{A\cap B}{A \cup B}IoU=A∪BA∩B
我們將用這個概念來描述兩個框之間的重合度。兩個框可以看成是兩個像素的集合,它們的交并比等于兩個框重合部分的面積除以它們合并起來的面積。下圖a中紅色區域是兩個框的重合面積,圖b中藍色區域是兩個框的相并面積。用這兩個面積相除即可得到它們之間的交并比,如?圖5?所示。
圖5:交并比
假設兩個矩形框A和B的位置分別為:
A:[xa1,ya1,xa2,ya2]A: [x_{a1}, y_{a1}, x_{a2}, y_{a2}]a[xa1,ya1,xa2,ya2]
?????????????????????????????????????????????b[xb1,yb1,xb2,yb2]
假如位置關系如?圖6?所示:?
圖6:計算交并比
如果二者有相交部分,則相交部分左上角坐標為:
x1=max(xa1,xb1),?????y1=max(ya1,yb1)x_1 = max(x_{a1}, x_{b1}), \ \ \ \ \ y_1 = max(y_{a1}, y_{b1})x1=max(xa1,xb1),?????y1=max(ya1,yb1)
據讀取和預處理
數據預處理是訓練神經網絡時非常重要的步驟。合適的預處理方法,可以幫助模型更好的收斂并防止過擬合。首先需要從磁盤讀入數據,然后需要對這些數據進行預處理,為了保證網絡運行的速度通常還要對數據預處理進行加速。
數據讀取
前面已經將圖片的所有描述信息保存在records中了,其中的每一個元素包含了一張圖片的描述,下面的程序展示了如何根據records里面的描述讀取圖片及標注。
### 數據讀取import cv2
def get_bbox(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))
gt_class2 = np.zeros((MAX_NUM,))
for i in range(len(gt_bbox)):
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 用相對值
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)record = records[0]
img, gt_boxes, gt_labels, scales = get_img_data_from_file(record)img.shape(1268, 1268, 3)gt_boxes.shape(50, 4)gt_labelsarray([1., 0., 2., 3., 4., 5., 5., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,?0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,?0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])scales(1268.0, 1268.0)
get_img_data_from_file()函數可以返回圖片數據的數據,它們是圖像數據img, 真實框坐標gt_boxes, 真實框包含的物體類別gt_labels, 圖像尺寸scales。
數據預處理
在計算機視覺中,通常會對圖像做一些隨機的變化,產生相似但又不完全相同的樣本。主要作用是擴大訓練數據集,抑制過擬合,提升模型的泛化能力,常用的方法見下面的程序。
隨機改變亮暗、對比度和顏色等
import numpy as npimport 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)
img = Image.fromarray(img)
img = ops[0](img)
img = ops[1](img)
img = ops[2](img)
img = np.asarray(img)
return img
隨機填充
# 隨機填充def random_expand(img,
gtboxes,
max_ratio=4.,
fill=None,
keep_ratio=True,
thresh=0.5):
if random.random() > thresh:
return img, gtboxes
if max_ratio < 1.0:
return img, gtboxes
h, w, c = img.shape
ratio_x = random.uniform(1, max_ratio)
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 = random.randint(0, ow - w)
off_y = random.randint(0, oh - h)
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[off_y:off_y + h, off_x:off_x + w, :] = img
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
return out_img.astype('uint8'), gtboxes
隨機裁剪
隨機裁剪之前需要先定義兩個函數,multi_box_iou_xywh和box_crop這兩個函數將被保存在box_utils.py文件中。
import numpy as npdef multi_box_iou_xywh(box1, box2):
"""
In this case, box1 or box2 can contain multi boxes.
Only two cases can be processed in this method:
1, box1 and box2 have the same shape, box1.shape == box2.shape
2, either box1 or box2 contains only one box, len(box1) == 1 or len(box2) == 1
If the shape of box1 and box2 does not match, and both of them contain multi boxes, it will be wrong.
"""
assert box1.shape[-1] == 4, "Box1 shape[-1] should be 4."
assert box2.shape[-1] == 4, "Box2 shape[-1] should be 4."
b1_x1, b1_x2 = box1[:, 0] - box1[:, 2] / 2, box1[:, 0] + box1[:, 2] / 2
b1_y1, b1_y2 = box1[:, 1] - box1[:, 3] / 2, box1[:, 1] + box1[:, 3] / 2
b2_x1, b2_x2 = box2[:, 0] - box2[:, 2] / 2, box2[:, 0] + box2[:, 2] / 2
b2_y1, b2_y2 = box2[:, 1] - box2[:, 3] / 2, box2[:, 1] + box2[:, 3] / 2
inter_x1 = np.maximum(b1_x1, b2_x1)
inter_x2 = np.minimum(b1_x2, b2_x2)
inter_y1 = np.maximum(b1_y1, b2_y1)
inter_y2 = np.minimum(b1_y2, b2_y2)
inter_w = inter_x2 - inter_x1
inter_h = inter_y2 - inter_y1
inter_w = np.clip(inter_w, a_min=0., a_max=None)
inter_h = np.clip(inter_h, a_min=0., a_max=None)
inter_area = inter_w * inter_h
b1_area = (b1_x2 - b1_x1) * (b1_y2 - b1_y1)
b2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1)
return inter_area / (b1_area + b2_area - inter_area)
def box_crop(boxes, labels, crop, img_shape):
x, y, w, h = map(float, crop)
im_w, im_h = map(float, img_shape)
boxes = boxes.copy()
boxes[:, 0], boxes[:, 2] = (boxes[:, 0] - boxes[:, 2] / 2) * im_w, (
boxes[:, 0] + boxes[:, 2] / 2) * im_w
boxes[:, 1], boxes[:, 3] = (boxes[:, 1] - boxes[:, 3] / 2) * im_h, (
boxes[:, 1] + boxes[:, 3] / 2) * im_h
crop_box = np.array([x, y, x + w, y + h])
centers = (boxes[:, :2] + boxes[:, 2:]) / 2.0
mask = np.logical_and(crop_box[:2] <= centers, centers <= crop_box[2:]).all(
axis=1)
boxes[:, :2] = np.maximum(boxes[:, :2], crop_box[:2])
boxes[:, 2:] = np.minimum(boxes[:, 2:], crop_box[2:])
boxes[:, :2] -= crop_box[:2]
boxes[:, 2:] -= crop_box[:2]
mask = np.logical_and(mask, (boxes[:, :2] < boxes[:, 2:]).all(axis=1))
boxes = boxes * np.expand_dims(mask.astype('float32'), axis=1)
labels = labels * mask.astype('float32')
boxes[:, 0], boxes[:, 2] = (boxes[:, 0] + boxes[:, 2]) / 2 / w, (
boxes[:, 2] - boxes[:, 0]) / w
boxes[:, 1], boxes[:, 3] = (boxes[:, 1] + boxes[:, 3]) / 2 / h, (
boxes[:, 3] - boxes[:, 1]) / h
return boxes, labels, mask.sum()# 隨機裁剪
def random_crop(img,
boxes,
labels,
scales=[0.3, 1.0],
max_ratio=2.0,
constraints=None,
max_trial=50):
if len(boxes) == 0:
return img, boxes
if not constraints:
constraints = [(0.1, 1.0), (0.3, 1.0), (0.5, 1.0), (0.7, 1.0),
(0.9, 1.0), (0.0, 1.0)]
img = Image.fromarray(img)
w, h = img.size
crops = [(0, 0, w, h)]
for min_iou, max_iou in constraints:
for _ in range(max_trial):
scale = random.uniform(scales[0], scales[1])
aspect_ratio = random.uniform(max(1 / max_ratio, scale * scale), \
min(max_ratio, 1 / scale / scale))
crop_h = int(h * scale / np.sqrt(aspect_ratio))
crop_w = int(w * scale * np.sqrt(aspect_ratio))
crop_x = random.randrange(w - crop_w)
crop_y = random.randrange(h - crop_h)
crop_box = np.array([[(crop_x + crop_w / 2.0) / w,
(crop_y + crop_h / 2.0) / h,
crop_w / float(w), crop_h / float(h)]])
iou = multi_box_iou_xywh(crop_box, boxes)
if min_iou <= iou.min() and max_iou >= iou.max():
crops.append((crop_x, crop_y, crop_w, crop_h))
break
while crops:
crop = crops.pop(np.random.randint(0, len(crops)))
crop_boxes, crop_labels, box_num = box_crop(boxes, labels, crop, (w, h))
if box_num < 1:
continue
img = img.crop((crop[0], crop[1], crop[0] + crop[2],
crop[1] + crop[3])).resize(img.size, Image.LANCZOS)
img = np.asarray(img)
return img, crop_boxes, crop_labels
img = np.asarray(img)
return img, boxes, labels
隨機縮放
# 隨機縮放def random_interp(img, size, interp=None):
interp_method = [
cv2.INTER_NEAREST,
cv2.INTER_LINEAR,
cv2.INTER_AREA,
cv2.INTER_CUBIC,
cv2.INTER_LANCZOS4,
]
if not interp or interp not in interp_method:
interp = interp_method[random.randint(0, len(interp_method) - 1)]
h, w, _ = img.shape
im_scale_x = size / float(w)
im_scale_y = size / float(h)
img = cv2.resize(
img, None, None, fx=im_scale_x, fy=im_scale_y, interpolation=interp)
return img
隨機翻轉
# 隨機翻轉def random_flip(img, gtboxes, thresh=0.5):
if random.random() > thresh:
img = img[:, ::-1, :]
gtboxes[:, 0] = 1.0 - gtboxes[:, 0]
return img, gtboxes
隨機打亂真實框排列順序
# 隨機打亂真實框排列順序def shuffle_gtbox(gtbox, gtlabel):
gt = np.concatenate(
[gtbox, gtlabel[:, np.newaxis]], axis=1)
idx = np.arange(gt.shape[0])
np.random.shuffle(idx)
gt = gt[idx, :]
return gt[:, :4], gt[:, 4]
批量數據讀取與加速
上面的程序展示了如何讀取一張圖片的數據并加速,下面的代碼實現了批量數據讀取。
# 獲取一個批次內樣本隨機縮放的尺寸def get_img_size(mode):
if (mode == 'train') or (mode == 'valid'):
inds = np.array([0,1,2,3,4,5,6,7,8,9])
ii = np.random.choice(inds)
img_size = 320 + ii * 32
else:
img_size = 608
return img_size
# 將 list形式的batch數據 轉化成多個array構成的tuple
def make_array(batch_data):
img_array = np.array([item[0] for item in batch_data], dtype = 'float32')
gt_box_array = np.array([item[1] for item in batch_data], dtype = 'float32')
gt_labels_array = np.array([item[2] for item in batch_data], dtype = 'int32')
img_scale = np.array([item[3] for item in batch_data], dtype='int32')
return img_array, gt_box_array, gt_labels_array, img_scale
# 批量讀取數據,同一批次內圖像的尺寸大小必須是一樣的,
# 不同批次之間的大小是隨機的,
# 由上面定義的get_img_size函數產生
def data_loader(datadir, batch_size= 10, mode='train'):
cname2cid = get_insect_names()
records = get_annotations(cname2cid, datadir)
def reader():
if mode == 'train':
np.random.shuffle(records)
batch_data = []
img_size = get_img_size(mode)
for record in records:
#print(record)
img, gt_bbox, gt_labels, im_shape = get_img_data(record,
size=img_size)
batch_data.append((img, gt_bbox, gt_labels, im_shape))
if len(batch_data) == batch_size:
yield make_array(batch_data)
batch_data = []
img_size = get_img_size(mode)
if len(batch_data) > 0:
yield make_array(batch_data)
return readerd = data_loader('/home/aistudio/work/insects/train', batch_size=2, mode='train')img, gt_boxes, gt_labels, im_shape = next(d())img.shape, gt_boxes.shape, gt_labels.shape, im_shape.shape((2, 3, 544, 544), (2, 50, 4), (2, 50), (2, 2))
由于在數據預處理耗時較長,可能會成為網絡訓練速度的瓶頸,所以需要對預處理部分進行優化。通過使用Paddle提供的API paddle.reader.xmap_readers可以開啟多線程讀取數據,具體實現代碼如下。
import functoolsimport paddle
# 使用paddle.reader.xmap_readers實現多線程讀取數據
def multithread_loader(datadir, batch_size= 10, mode='train'):
cname2cid = get_insect_names()
records = get_annotations(cname2cid, datadir)
def reader():
if mode == 'train':
np.random.shuffle(records)
img_size = get_img_size(mode)
batch_data = []
for record in records:
batch_data.append((record, img_size))
if len(batch_data) == batch_size:
yield batch_data
batch_data = []
img_size = get_img_size(mode)
if len(batch_data) > 0:
yield batch_data
def get_data(samples):
batch_data = []
for sample in samples:
record = sample[0]
img_size = sample[1]
img, gt_bbox, gt_labels, im_shape = get_img_data(record, size=img_size)
batch_data.append((img, gt_bbox, gt_labels, im_shape))
return make_array(batch_data)
mapper = functools.partial(get_data, )
return paddle.reader.xmap_readers(mapper, reader, 8, 10)d = multithread_loader('/home/aistudio/work/insects/train', batch_size=2, mode='train')img, gt_boxes, gt_labels, im_shape = next(d())img.shape, gt_boxes.shape, gt_labels.shape, im_shape.shape((2, 3, 352, 352), (2, 50, 4), (2, 50), (2, 2))
至此,我們完成了如何查看數據集中的數據、提取數據標注信息、從文件讀取圖像和標注數據、數據增多、批量讀取和加速等過程,通過multithread_loader可以返回img, gt_boxes, gt_labels, im_shape等數據,接下來就可以將它們輸入神經網絡應用在具體算法上面了。
在開始具體的算法講解之前,先補充一下測試數據的讀取代碼,測試數據沒有標注信息,代碼如下所示。
# 測試數據讀取# 將 list形式的batch數據 轉化成多個array構成的tuple
def make_test_array(batch_data):
img_name_array = np.array([item[0] for item in batch_data])
img_data_array = np.array([item[1] for item in batch_data], dtype = 'float32')
img_scale_array = np.array([item[2] for item in batch_data], dtype='int32')
return img_name_array, img_data_array, img_scale_array
# 測試數據讀取
def test_data_loader(datadir, batch_size= 10, test_image_size=608, mode='test'):
"""
加載測試用的圖片,測試數據沒有groundtruth標簽
"""
image_names = os.listdir(datadir)
def reader():
batch_data = []
img_size = test_image_size
for image_name in image_names:
file_path = os.path.join(datadir, image_name)
img = cv2.imread(file_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
H = img.shape[0]
W = img.shape[1]
img = cv2.resize(img, (img_size, img_size))
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
mean = np.array(mean).reshape((1, 1, -1))
std = np.array(std).reshape((1, 1, -1))
out_img = (img / 255.0 - mean) / std
out_img = out_img.astype('float32').transpose((2, 0, 1))
img = out_img #np.transpose(out_img, (2,0,1))
im_shape = [H, W]
batch_data.append((image_name.split('.')[0], img, im_shape))
if len(batch_data) == batch_size:
yield make_test_array(batch_data)
batch_data = []
if len(batch_data) > 0:
yield make_test_array(batch_data)
return reader
總結
以上是生活随笔為你收集整理的处理之后的图像句柄传到显示框_深度学习目标检测与预处理模块的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 部队团长年龄限制
- 下一篇: maskrcnn用于目标检测_用于目标检