为行人检测项目添加一个可视化检测界面,利用训练的yolov5来设计一个qt界面
设计界面
打开Qt designer,自定义控件摆放位置,
摆放完控件后保存得到一个ui文件,利用Pyuic工具转变为.py文件。
新建立一个.py文件负责界面控制,使用connect,将按钮与操作对应起来。
打开图片
打开权重文件
加载相关参数,并初始化模型
这部分是将detct.py文件进行修改得到
#加载相关参数,并初始化模型
def model_init(self):
#11111
parser = argparse.ArgumentParser()
parser.add_argument('--weights', nargs='+', type=str,
default=r'D:\yolov5\yolov5-5.0\yolov5-5.0\runs\train\exp11\weights\best.pt',
help='model.pt path(s)')
parser.add_argument('--source', type=str, default=r'D:\yolov5\yolov5-5.0\yolov5-5.0\three.jpg', help='source') # file/folder, 0 for webcam
parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
parser.add_argument('--view-img', action='store_true', help='display results')
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
parser.add_argument('--augment', action='store_true', help='augmented inference')
parser.add_argument('--update', action='store_true', help='update all models')
parser.add_argument('--project', default='runs/detect', help='save results to project/name')
parser.add_argument('--name', default='exp', help='save results to project/name')
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
self.opt = parser.parse_args()
print(self.opt)
#22222
source, weights, view_img, save_txt, imgsz =self.opt.source, self.opt.weights, self.opt.view_img, self.opt.save_txt, self.opt.img_size
#save_img = not opt.nosave and not source.endswith('.txt') # save inference images
# 若openfile_name_model不为空,则使用此权重进行初始化
if self.openfile_name_model:
weights = self.openfile_name_model
print("Using button choose model")
self.device = select_device(self.opt.device)
self.half = self.device.type != 'cpu' # half precision only supported on CUDA
cudnn.benchmark = True
# Load model
self.model = attempt_load(weights, map_location=self.device) # load FP32 model
stride = int(self.model.stride.max()) # model stride
self.imgsz = check_img_size(imgsz, s=stride) # check img_size
if self.half:
self.model.half() # to FP16
# Get names and colors
self.names = self.model.module.names if hasattr(self.model, 'module') else self.model.names
self.colors = [[random.randint(0, 255) for _ in range(3)] for _ in self.names]
print("model initial done")
# 设置提示框
QtWidgets.QMessageBox.information(self, u"Notice", u"模型加载完成", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
#11111部分改自detect.py文件以下部分,注意要将所有的opt前加上self #22222部分改自detect.py文件以下部分,因为加入了可以选择权重文件的步骤,所以加了对openfile_name_model是否为空的判断,注意添加self.
检测
一个检测的方法,没有直接与控件链接,而是在选择是图片或视频检测时在调用
def detect(self, name_list, img):
'''
:param name_list: 文件名列表
:param img: 待检测图片
:return: info_show:检测输出的文字信息
'''
showimg = img
with torch.no_grad():
img = letterbox(img, new_shape=self.opt.img_size)[0]
# Convert
img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
img = np.ascontiguousarray(img)
img = torch.from_numpy(img).to(self.device)
img = img.half() if self.half else img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if img.ndimension() == 3:
img = img.unsqueeze(0)
# Inference
pred = self.model(img, augment=self.opt.augment)[0]
# Apply NMS
pred = non_max_suppression(pred, self.opt.conf_thres, self.opt.iou_thres, classes=self.opt.classes,
agnostic=self.opt.agnostic_nms)
info_show = ""
# Process detections
num=0
for i, det in enumerate(pred):
if det is not None and len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], showimg.shape).round()
for *xyxy, conf, cls in reversed(det):
# label = '%s %.2f' % (self.names[int(cls)], conf)
# name_list.append(self.names[int(cls)])
# label = f'{self.names[int(cls)]} {conf:.2f}'
if self.names[int(cls)]=='person':
label = '%s %.2f' % (self.names[int(cls)], conf)
name_list.append(self.names[int(cls)])
plot_one_box(xyxy, showimg, label=label, color=self.colors[int(cls)], line_thickness=3)
num+=1
print(num)
return num
检测图片
def button_image_open(self):
print('button_image_open')
name_list = []
try:
img_name, _ = QFileDialog.getOpenFileName(self, '请选择图片', '.', '图像文件(*.jpg *.jpeg *.png, *.mp4)')
except OSError as reason:
print('文件打开出错啦!核对路径是否正确'+ str(reason))
else:
# 判断图片是否为空
if not img_name:
QtWidgets.QMessageBox.warning(self, u"Warning", u"打开图片失败", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
else:
img = cv2.imread(img_name)
print("img_name:", img_name)
# self.label.setPixmap(QtGui.QPixmap.fromImage(self.QtImg))
# self.label.setScaledContents(True)
info_show = self.detect(name_list, img)
self.label_3.setText("检测到的人数:"+str(info_show))
# print(info_show)
self.label_2.setScaledContents(True) # 需要在图片显示之前进行设置
self.label_2.setPixmap(QPixmap(img_name)) # 运行之后QLabel就可以自适应显示图片了
# 获取当前系统时间,作为img文件名
now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
file_extension = img_name.split('.')[-1]
# new_filename
new_filename = str(now) + '.' + file_extension # 获得文件后缀名
# file_path = self.output_folder + 'img_output/' + new_filename
# file_path = self.output_folder + 'img_output/'
file_path = self.output_folder + 'img_output/' + new_filename
print(file_path)
cv2.imwrite(file_path, img)
self.label_4.setText("输出结果存储到:" + file_path)
# 检测结果显示在界面
# self.textBrowser.setText(info_show)
self.result = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
self.result = cv2.resize(self.result, (640, 480), interpolation=cv2.INTER_AREA)
self.QtImg = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0],
QtGui.QImage.Format_RGB32)
self.label.setPixmap(QtGui.QPixmap.fromImage(self.QtImg))
self.label.setScaledContents(True) # 设置图像自适应界面大小
读取视频
def button_video_open(self):
video_name, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开视频", "yolov5-5.0/", "*.mp4;;*.avi;;All Files(*)")
flag = self.cap.open(video_name)
if not flag:
QtWidgets.QMessageBox.warning(self, u"Warning", u"打开视频失败", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
else:
# -------------------------写入视频----------------------------------#
fps, w, h, save_path = self.set_video_name_and_path()
self.vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
self.label_4.setText("输出结果存储到:" + save_path)
self.timer_video.start(30) # 以30ms为间隔,启动或重启定时器
# 进行视频识别时,关闭其他按键点击功能
self.pushButton.setDisabled(True)
self.pushButton_2.setDisabled(True)
self.pushButton_5.setDisabled(True)
读取摄像头
# 打开摄像头检测
def button_camera_open(self):
print("Open camera to detect")
# 设置使用的摄像头序号,系统自带为0
camera_num = 0
# 打开摄像头
self.cap = cv2.VideoCapture(camera_num)
# 判断摄像头是否处于打开状态
bool_open = self.cap.isOpened()
if not bool_open:
QtWidgets.QMessageBox.warning(self, u"Warning", u"打开摄像头失败", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
else:
fps, w, h, save_path = self.set_video_name_and_path()
fps = 5 # 控制摄像头检测下的fps,Note:保存的视频,播放速度有点快,我只是粗暴的调整了FPS
self.vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
self.label_4.setText("输出结果存储到:" + save_path)
self.timer_video.start(30)
self.pushButton.setDisabled(True)
self.pushButton_2.setDisabled(True)
self.pushButton_5.setDisabled(True)
读取时间与视频显示操作
读取时间用于视频和摄像头检查时使用时间作为结果存储的文件名,而视频帧操作是在labe控件上显示视频,在上面的读取视频和打开摄像头操作都设置有一个定时器,而定时器一旦超时就会定时器超时,就会将槽绑定至show_video_frame
def set_video_name_and_path(self):
# 获取当前系统时间,作为img和video的文件名
now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
# if vid_cap: # video
fps = self.cap.get(cv2.CAP_PROP_FPS)
w = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 视频检测结果存储位置
save_path = self.output_folder + 'video_output/' + now + '.mp4'
return fps, w, h, save_path
# 定义视频帧显示操作
def show_video_frame(self):
name_list = []
flag, img = self.cap.read()
show=img
if img is not None:
self.result = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
showImage = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0],
QtGui.QImage.Format_RGB888)
self.label_2.setPixmap(QtGui.QPixmap.fromImage(showImage))
self.label_2.setScaledContents(True) # 设置图像自适应界面大小
info_show = self.detect(name_list, img) # 检测结果写入到原始img上
self.label_3.setText("检测到的人数:" + str(info_show))
self.vid_writer.write(img) # 检测结果写入视频
print(info_show)
show=img
# show = cv2.resize(img, (491,400)) # 直接将原始img上的检测结果进行显示
self.result = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
showImage = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0],
QtGui.QImage.Format_RGB888)
self.label.setPixmap(QtGui.QPixmap.fromImage(showImage))
self.label.setScaledContents(True) # 设置图像自适应界面大小
else:
self.timer_video.stop()
# 读写结束,释放资源
self.cap.release() # 释放video_capture资源
self.vid_writer.release() # 释放video_writer资源
self.label.clear()
self.label_2.clear()
# 视频帧显示期间,禁用其他检测按键功能
self.pushButton.setDisabled(False)
self.pushButton_2.setDisabled(False)
self.pushButton_5.setDisabled(False)
参考了博主‘叼着狗骨头的猫’以及在B站的一些视频,本文章仅记录自己学习过程,防止忘得太快。