以下内容不包含模型的构建与训练,只包含用模型进行相关图片(狗子)的检测。
一、准备工作
新建项目文件夹DETECT(存放项目)下:
- 新建子文件夹dataset(存放下载的数据集)
- 新建子文件夹output(存放生成的输出文件)
- 待检测的图片
dataset下:
- 新建子文件夹images(存放下载的源图片)
- annotations(存放下载的标注文档)
- dog(存放下载的正样本)
- background(存放下载的负样本)
output下:
- model.h5(下载的训练好的模型)
- lb.pickle(生成的标签文件)
二、代码实现
##############################导入包#############################################
import os
import cv2
from tensorflow.keras.models import Model, model_from_json, load_model
from tensorflow.keras.preprocessing.image import img_to_array, load_img, array_to_img
from tensorflow.keras.utils import to_categorical
import pickle
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelBinarizer
from sklearn.utils import shuffle
##########################设置文件存放路径############################################
BASE_PATH = "dataset" #设置数据集的路径
ORIG_IMAGES = os.path.sep.join([BASE_PATH, "images"]) #设置源图片的路径
ORIG_ANNOTS = os.path.sep.join([BASE_PATH, "annotations"]) #设置标注文档的路径
DOG_PATHS = os.path.sep.join([BASE_PATH, "dog"])#设置正样本路径
BACKGROUND_PATHS = os.path.sep.join([BASE_PATH, "background"])#设置负样本路径
BASE_OUTPUT = "output" #设置输出文件的路径,如果没有则创建
if not os.path.exists(BASE_OUTPUT):
os.mkdir(BASE_OUTPUT)
LB_PATH = os.path.sep.join([BASE_OUTPUT, "lb.pickle"]) #LB_PATH 是保存标签文件的路径
############################定义检测时需要用到的函数###################################
def ssearch(image, mode="fast"):
ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation()
ss.setBaseImage(image)
if mode == "fast":
ss.switchToSelectiveSearchFast()
else:
ss.switchToSelectiveSearchQuality()
rects = ss.process()
return rects
def NMS(boxes, threshold):
if len(boxes) == 0:
return []
boxes = np.array(boxes).astype("float")
x1 = boxes[:, 0]
y1 = boxes[:, 1]
x2 = boxes[:, 2]
y2 = boxes[:, 3]
w1 = x2 - x1
h1 = y2 - y1
area = (w1 + 1) * (h1 + 1)
temp = []
idxs = np.argsort(y2)
while len(idxs) > 0:
last = len(idxs) - 1
i = idxs[last]
temp.append(i)
x1_m = np.maximum(x1[i], x1[idxs[:last]])
y1_m = np.maximum(y1[i], y1[idxs[:last]])
x2_m = np.minimum(x2[i], x2[idxs[:last]])
y2_m = np.minimum(y2[i], y2[idxs[:last]])
w = np.maximum(0, x2_m - x1_m + 1)
h = np.maximum(0, y2_m - y1_m + 1)
over = (w * h) / area[idxs[:last]]
idxs = np.delete(idxs, np.concatenate(([last],np.where(over > threshold)[0])))
return boxes[temp].astype("int")
#############################进行分类训练################################################
#获取每张正样本图片路径
dogs = os.listdir(DOG_PATHS)
dogs = [os.path.sep.join([DOG_PATHS, path]) for path in dogs]
#获取每张负样本路径
backgrounds = os.listdir(BACKGROUND_PATHS)
backgrounds = [os.path.sep.join([BACKGROUND_PATHS, path]) for path in backgrounds]
imagepaths = [] # 创建列表用于存放所有正负样本的路径
bboxes = [] # 存放矩形坐标框
labels = [] # 存放所有图片对应的标签
for dog in dogs:
imagepaths.append(dog) #将正样本中的每张图片添加到 imagepaths
labels.append("dog") #将每张图片对应的标签 "dog" 添加到 labels
bboxes.append((0.0, 0.0, 0.0, 0.0)) #添加对应的矩形框坐标 bboxes
for background in backgrounds:
imagepaths.append(background) #将每张负样本的路径添加到 imagepaths
labels.append("background") #添加对应的标签 "background" 到 labels
bboxes.append((0.0, 0.0, 0.0, 0.0))#添加对应的矩形框坐标
labels, imagepaths, bboxes = shuffle(labels, imagepaths, bboxes, random_state=42)#打乱labels、imagepaths、bboxes
lb = LabelBinarizer() #对数据集的标签进行编码
labels = lb.fit_transform(labels) #将训练集和测试集的标签二值化
if len(lb.classes_) == 2:
labels = to_categorical(labels)
#使用导入的 pickle 模块将标签对象 lb 转换为二进制对象并保存成lb.pickle文件
with open(LB_PATH, "wb") as f:
f.write(pickle.dumps(lb))
#############################载入模型################################################
model = load_model(os.path.sep.join([BASE_OUTPUT, "dog_model.h5"])) #载入模型
lb = pickle.loads(open(LB_PATH, "rb").read()) #读取标签对象lb.pickle
image = load_img("dogs6.jpeg") #导入待检测图片
image = img_to_array(image) #将图片转换为数组
height, width = image.shape[:2] #获取图片的宽 width 和高 height
rects = ssearch(image) #使用选择性搜索获取候选区域
pboxes = [] #列表 pboxes 将会存储模型输出的检测框
for (x, y, w, h) in rects[:500]: #遍历 rects 中的前 500 个候选区域
if w < 50 or h < 50 or w == 0 or h == 0 or 2 * w < h or w > 0.7 * width or h > 0.7 * height:
continue #剔除宽和高不满足设定条件的候选区域
roi = image[y:y + h, x:x + w]#从原候选区域中裁剪出roi区域
roi = cv2.resize(roi, (224, 224), interpolation=cv2.INTER_LINEAR)#对roi区域进行尺寸缩放
roi = roi / 255.0#对roi区域进行归一化处理
roi = np.expand_dims(roi, axis=0)#扩展roi区域维度
(boxPreds, labelPreds) = model.predict(roi) #用模型对roi区域进行检测, 输出检测框的坐标 boxPreds 和目标的类别 labelPreds。
i = np.argmax(labelPreds, axis=1)
label = lb.classes_[i][0]
if label == "background": #如果标签的预测结果是backgournd则跳出当前循环
continue
if labelPreds[0][i] < 0.6: #如果标签的预测值小于 0.6 则跳出当前循环
continue
(startX, startY, endX, endY) = boxPreds[0] #获取检测框的左上角和右下角顶点坐标
#计算检测框在候选区域中的绝对坐标
startX = int(startX * w)
startY = int(startY * h)
endX = int(endX * w)
endY = int(endY * h)
#计算检测框的宽 r_w 和高 r_h
r_w = endX - startX
r_h = endY - startY
#计算检测框在原图中的坐标
s_x = x + startX
s_y = y + startY
e_x = s_x + r_w
e_y = s_y + r_h
#将转换后的坐标添加到 pboxes 中
pboxes.append((s_x, s_y, e_x, e_y))
picks = NMS(pboxes, threshold=0.3) #使用非极大值抑制抑制剔除 pboxes 中的冗余检测框
image = np.array(array_to_img(image)) #将数组转换为图像
for (nms_x, nms_y, end_x, end_y) in picks: #在原图中画出检测框
cv2.rectangle(image, (nms_x, nms_y), (end_x, end_y), (0, 255, 0), 2)
#显示检测结果
plt.figure(figsize=(10, 10))
plt.imshow(image)
#保存图片至项目文件夹下
image = image[:, :, ::-1]
results = imagepath.split(".")[0] + "_predict" + ".jpg"
cv2.imwrite(results, image)
三、检测结果