OpenCV从零开始(二)——物体检测,框出物体轮廓,设计交互界面

OpenCV 小白冲冲冲

  • 初始化模型
  • 物体检测
  • 在画面窗口显示识别框和结果
  • 改变摄像头分辨率
  • 设计交互界面
    • 创建新窗口并添加鼠标响应函数
    • 在画面上创建按钮图标
    • 修改鼠标响应函数
    • 增加条件判断
  • 完整工程

参考: 使用Opencv与Python搭建自己的物体检测软件.


根据参考视频里的内容完全可以自己搭建出来,但由于视频是全英文,而且机翻出来的字幕不太准确,所以把步骤和代码注释做成笔记整理了下来,完整工程也附在了文末。

初始化模型

导入的模型库是训练好的,可检测80种物体。

# 初始化模型
net = cv2.dnn.readNet("dnn_model/yolov4-tiny.weights",
                      "dnn_model/yolov4-tiny.cfg")    # 导入模型
model = cv2.dnn_DetectionModel(net)
model.setInputParams(size=(320, 320), scale=1 / 255)  # 压缩图片至DNN可处理的尺寸,尺寸越大检测效果越好,但处理速度会变慢

物体检测

# 物体检测
(class_ids, scores, bboxes) = model.detect(frame)
print("class ids", class_ids)  # 分类结果
print("scores", scores)        # 准确度
print("bboxes", bboxes)        # 识别框的位置信息

在画面窗口显示识别框和结果

  • 导入识别分类表
# 将识别分类表赋值到classes
classes = []
with open("dnn_model/classes.txt", "r") as file_object:   # 只读模式读取文件
    for class_name in file_object.readlines():     # 将识别结果赋值到数组
        class_name = class_name.strip()    # 去掉头尾的空格或换行
        classes.append(class_name)         # 将class_name加入到classes里
  • 窗口显示识别框和文本结果
# 在画面上显示识别框和识别结果
for class_id, score, bbox in zip(class_ids, scores, bboxes):        # 将多帧数据打包重组成单帧
    (x, y, w, h) = bbox   # x,y代表识别框左上角位置坐标,w宽度,h高度
    class_name = classes[class_id]

    # 在画面上画出矩形框框住物体,用文本显示识别结果
    cv2.rectangle(frame, (x, y), (x + w, y + h), (128, 42, 42), 3)  # 画面,左上角坐标,右下角坐标,RGB颜色,厚度
    cv2.putText(frame, class_name, (x, y - 10), cv2.FONT_HERSHEY_PLAIN, 2, (128, 42, 42), 2)  # 画面,文本内容,位置,字体,字体大小,RGB颜色,厚度

改变摄像头分辨率

至此,已经初步完成了物体检测。要想调整处理速度和识别准确度间的关系,可以改变摄像头分辨率或者模型的大小。比如,在初始化摄像头代码中添加一些设置语句。

# 初始化摄像头
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

设计交互界面

视频里的小哥哥还教了如何增加交互性,最终达到的效果是,通过点击画面上的按钮来控制是否要显示物体检测结果,即是否把物体框出。
可以自己来写代码,或者直接调用人家写好的函数。这里只展示怎么靠自己来设计。

创建新窗口并添加鼠标响应函数

# 定义鼠标响应回调函数
def click_button(event, x, y, flags, params):
    if event == cv2.EVENT_LBUTTONDOWN:
        print(x, y)

# 创建新窗口
cv2.namedWindow("Frame")
cv2.setMouseCallback("Frame", click_button)

在画面上创建按钮图标

这里用 fillPoly 替代 rectangle 可以加快数据处理速度,注意的是使用前要在最前面加一句 import numpy as np

# 在画面上创建按钮
# cv2.rectangle(frame, (20, 20), (210, 70), (0, 0, 200), -1)      # 边框厚度为-1表示用颜色填充这个框框
polygon = np.array([[(20, 20), (210, 20), (210, 70), (20, 70)]])  # 通过绘制多边形来创建按钮
cv2.fillPoly(frame, polygon, (0, 0, 200))
cv2.putText(frame, "Person", (30,60), cv2.FONT_HERSHEY_PLAIN, 3, (255, 255, 255), 3)

修改鼠标响应函数

  • pointPolygonTest 函数的用法:用于检测一个点是否在多边形中
    • 一参 contour 代表多边形
    • 二参 pt 代表测试点
    • 三参 measureDist 布尔变量
      当measureDist设置为true时,返回实际距离值。
      若返回值为正,表示点在多边形内部;返回值为负,表示在多边形外部,返回值为0,表示在多边形上。
      当measureDist设置为false时,返回 -1、0、1三个固定值。
      若返回值为+1,表示点在多边形内部;返回值为-1,表示在多边形外部;返回值为0,表示在多边形上。
button_person = False   # 用以标志按钮状态

# 定义鼠标响应回调函数
def click_button(event, x, y, flags, params):
    global button_person
    if event == cv2.EVENT_LBUTTONDOWN:
        print(x, y)
        polygon = np.array([[(20, 20), (210, 20), (210, 70), (20, 70)]])  # 通过绘制多边形来创建按钮

        is_inside = cv2.pointPolygonTest(polygon, (x, y), False)
        if is_inside > 0:
            print("按钮被点击")

            # 改变按钮状态标志位
            if button_person:
                button_person = False
            else:
                button_person = True

            print("按钮状态为", button_person)

增加条件判断

添加条件判断语句,根据按钮的状态标志位决定是否显示识别框和结果

if class_name == "person" and button_person is True:
    # 在画面上画出矩形框框住物体,用文本显示识别结果
    cv2.rectangle(frame, (x, y), (x + w, y + h), (128, 42, 42), 3)  # 画面,左上角坐标,右下角坐标,RGB颜色,厚度
    cv2.putText(frame, class_name, (x, y - 10), cv2.FONT_HERSHEY_PLAIN, 2, (128, 42, 42), 2)  # 画面,文本内容,位置,字体,字体大小,RGB颜色,厚度

完整工程

完整工程的链接在这里 link. (没有设置积分要求哦)

你可能感兴趣的:(opencv,交互,计算机视觉)