一步一步学OAK之六:通过OAK相机实现特征检测

特征检测

特征检测是指在数字图像中自动寻找并定位具有特定特征的图像区域或图像点。这些特征可以是在图像中具有独特外观、结构或统计特性的对象、边缘、角点、纹理等。

特征检测在计算机视觉中起到至关重要的作用,它是许多计算机视觉任务的基础,如目标检测、跟踪、姿态估计、图像拼接、图像识别等。通过提取和匹配特征,可以实现图像对齐、物体识别、运动估计等计算机视觉任务。

传统的特征检测方法包括边缘检测(如Canny边缘检测)、角点检测(如Harris角点检测)、尺度不变特征变换(SIFT)、加速稳健特征(SURF)等。近年来,基于深度学习的特征检测方法,如卷积神经网络(CNN)的特征提取,也在图像特征检测领域取得了重要的突破和应用。

Setup 1: 创建文件

  • 创建新建6-feature-detector文件夹
  • 用vscode打开该文件夹
  • 新建一个main.py 文件

Setup 2: 安装依赖

安装依赖前需要先创建和激活虚拟环境,我这里已经创建了虚拟环境OAKenv,在终端中输入cd…退回到OAKenv的根目录,输入 OAKenv\Scripts\activate激活虚拟环境

安装pip依赖项:

pip install numpy opencv-python depthai blobconverter --user

Setup 3: 导入需要的包

在main.py中导入项目需要的包

import cv2
import depthai as dai

Setup 4: 创建pipeline

pipeline = dai.Pipeline()

Setup 5: 创建节点

创建相机节点

monoLeft = pipeline.createMonoCamera()
monoRight = pipeline.createMonoCamera()
  1. monoLeft = pipeline.createMonoCamera():创建一个单通道相机节点(MonoCamera),用于捕获左侧单色图像。
  2. monoRight = pipeline.createMonoCamera():创建一个单通道相机节点(MonoCamera),用于捕获右侧单色图像。

创建特征检测节点

featureTrackerLeft = pipeline.createFeatureTracker()
featureTrackerRight = pipeline.createFeatureTracker()

这段代码在pipeline中创建三个边缘检测节点。

  1. featureTrackerLeft = pipeline.createFeatureTracker():创建了一个特征检测节点用于处理左侧单色图像。
  2. featureTrackerRight = pipeline.createFeatureTracker():创建了一个特征检测节点用于处理右侧单色图像。

创建数据交互的节点

xoutPassthroughFrameLeft = pipeline.createXLinkOut()
xoutTrackedFeaturesLeft = pipeline.createXLinkOut()
xoutPassthroughFrameRight = pipeline.createXLinkOut()
xoutTrackedFeaturesRight = pipeline.createXLinkOut()
xinTrackedFeaturesConfig = pipeline.createXLinkIn()

xoutPassthroughFrameLeft.setStreamName("passthroughFrameLeft")
xoutTrackedFeaturesLeft.setStreamName("trackedFeaturesLeft")
xoutPassthroughFrameRight.setStreamName("passthroughFrameRight")
xoutTrackedFeaturesRight.setStreamName("trackedFeaturesRight")
xinTrackedFeaturesConfig.setStreamName("trackedFeaturesConfig")

这段代码创建了五个与外部设备进行数据交互的节点。

  1. 使用pipeline.createXLinkOut()方法创建了四个用于与外部设备进行数据输出的节点。
  2. 使用pipeline.createXLinkIn()方法创建了一个用于与外部设备进行数据输入的节点。

为每个输出节点和输入节点设置了一个数据流名称

使用.setStreamName("name")方法为上面创建的五个节点设置数据流名称

Setup 6:设置相关属性

设置相机的相关属性

monoLeft.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
monoLeft.setBoardSocket(dai.CameraBoardSocket.LEFT)
monoRight.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
monoRight.setBoardSocket(dai.CameraBoardSocket.RIGHT)

对于左侧的单目相机:

  1. monoLeft.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P):设置了左侧单目相机的分辨率为400P。这意味着左侧单目相机将以400P的分辨率进行图像捕获。
  2. monoLeft.setBoardSocket(dai.CameraBoardSocket.LEFT):设置了左侧单目相机的板载插槽为LEFT。这表明左侧单目相机将通过与系统板的左侧接口连接。

对于右侧的单目相机:

  1. monoRight.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P):设置了右侧单目相机的分辨率为400P。这意味着右侧单目相机将以400P的分辨率进行图像捕获。
  2. monoRight.setBoardSocket(dai.CameraBoardSocket.RIGHT):设置了右侧单目相机的板载插槽为RIGHT。这表明右侧单目相机将通过与系统板的右侧接口连接。

设置特征检测器的初始配置

featureTrackerLeft.initialConfig.setMotionEstimator(False)
featureTrackerRight.initialConfig.setMotionEstimator(False)

禁用特征跟踪器实例中的运动估计器。

对于每个特征跟踪器实例,initialConfig是其初始化配置对象,可以用于设置不同的参数。

通过调用setMotionEstimator(False)函数并传递False作为参数,禁用运动估计器。这意味着特征跟踪器将不会对物体或特征点之间的运动进行估计,而只会跟踪它们的位置。

Setup 7: 建立链接关系

建立相机和特征跟踪器之间的数据连接

monoLeft.out.link(featureTrackerLeft.inputImage)
featureTrackerLeft.passthroughInputImage.link(xoutPassthroughFrameLeft.input)
featureTrackerLeft.outputFeatures.link(xoutTrackedFeaturesLeft.input)
xinTrackedFeaturesConfig.out.link(featureTrackerLeft.inputConfig)

monoRight.out.link(featureTrackerRight.inputImage)
featureTrackerRight.passthroughInputImage.link(xoutPassthroughFrameRight.input)
featureTrackerRight.outputFeatures.link(xoutTrackedFeaturesRight.input)
xinTrackedFeaturesConfig.out.link(featureTrackerRight.inputConfig)

featureTrackerConfig = featureTrackerRight.initialConfig.get()

print("Press 's' to switch between Harris and Shi-Thomasi corner detector!")
  1. monoLeft.out.link(featureTrackerLeft.inputImage):将左侧单目相机的输出链接到左侧特征跟踪器的输入图像。
  2. featureTrackerLeft.passthroughInputImage.link(xoutPassthroughFrameLeft.input):将左侧特征跟踪器的输入图像链接到左侧帧输出(用于传递原始输入图像到输出管道)。
  3. featureTrackerLeft.outputFeatures.link(xoutTrackedFeaturesLeft.input):将左侧特征跟踪器的输出特征链接到左侧被跟踪特征的输出。
  4. xinTrackedFeaturesConfig.out.link(featureTrackerLeft.inputConfig):将被跟踪特征的配置链接到左侧特征跟踪器的输入配置。
  5. monoRight.out.link(featureTrackerRight.inputImage):将右侧单目相机的输出链接到右侧特征跟踪器的输入图像。
  6. featureTrackerRight.passthroughInputImage.link(xoutPassthroughFrameRight.input):将右侧特征跟踪器的输入图像链接到右侧帧输出。
  7. featureTrackerRight.outputFeatures.link(xoutTrackedFeaturesRight.input):将右侧特征跟踪器的输出特征链接到右侧被跟踪特征的输出。
  8. xinTrackedFeaturesConfig.out.link(featureTrackerRight.inputConfig):将被跟踪特征的配置链接到右侧特征跟踪器的输入配置。
  9. featureTrackerRight.initialConfig.get():用于获取featureTrackerRight特征跟踪器实例的初始配置。

这段代码片段主要用于创建数据流水线,将图像数据从相机传递到特征跟踪器,并将跟踪的特征输出到相应的目标。

Setup 8: 连接设备并启动管道

with dai.Device(pipeline) as device:

Setup 9: 创建与DepthAI设备通信的输入队列和输出队列

    passthroughImageLeftQueue = device.getOutputQueue("passthroughFrameLeft", 8, False)
    outputFeaturesLeftQueue = device.getOutputQueue("trackedFeaturesLeft", 8, False)
    passthroughImageRightQueue = device.getOutputQueue("passthroughFrameRight", 8, False)
    outputFeaturesRightQueue = device.getOutputQueue("trackedFeaturesRight", 8, False)

    inputFeatureTrackerConfigQueue = device.getInputQueue("trackedFeaturesConfig")
    
    leftWindowName = "left"
    rightWindowName = "right"
  1. passthroughImageLeftQueue = device.getOutputQueue("passthroughFrameLeft", 8, False):从设备中获取名为"passthroughFrameLeft"的输出队列。该队列将用于接收左侧特征跟踪器的帧图像数据,队列大小为8,不使用阻塞模式。
  2. outputFeaturesLeftQueue = device.getOutputQueue("trackedFeaturesLeft", 8, False):从设备中获取名为"trackedFeaturesLeft"的输出队列。该队列将用于接收左侧特征跟踪器的跟踪特征数据,队列大小为8,不使用阻塞模式。
  3. passthroughImageRightQueue = device.getOutputQueue("passthroughFrameRight", 8, False):从设备中获取名为"passthroughFrameRight"的输出队列。该队列将用于接收右侧特征跟踪器的帧图像数据,队列大小为8,不使用阻塞模式。
  4. outputFeaturesRightQueue = device.getOutputQueue("trackedFeaturesRight", 8, False):从设备中获取名为"trackedFeaturesRight"的输出队列。该队列将用于接收右侧特征跟踪器的跟踪特征数据,队列大小为8,不使用阻塞模式。
  5. inputFeatureTrackerConfigQueue = device.getInputQueue("trackedFeaturesConfig"):从设备中获取名为"trackedFeaturesConfig"的输入队列。该队列将用于向特征跟踪器发送配置数据。

Setup 10: 定义drawFeatures的函数,用于在图像帧上绘制特征点

    def drawFeatures(frame, features):
        pointColor = (0, 0, 255)
        circleRadius = 2
        for feature in features:
            cv2.circle(frame, (int(feature.position.x), int(feature.position.y)), circleRadius, pointColor, -1, cv2.LINE_AA, 0)

定义了一个名为drawFeatures的函数,用于在图像帧上绘制特征点。

  1. pointColor = (0, 0, 255):定义了绘制特征点时使用的颜色。这里使用的是RGB颜色空间中的红色。

  2. circleRadius = 2:定义了绘制特征点时使用的圆的半径大小。

  3. for feature in features::对特征列表中的每个特征进行循环遍历。

  4. cv2.circle(frame, (int(feature.position.x), int(feature.position.y)), circleRadius, pointColor, -1, cv2.LINE_AA, 0):使用OpenCV的cv2.circle函数,在图像帧frame上绘制一个圆。圆心的位置由特征的位置(feature.position.x, feature.position.y)确定,圆的半径为circleRadius,颜色为pointColor,线宽为-1表示填充整个圆,cv2.LINE_AA指定使用抗锯齿线段,最后一个参数为0表示以默认的图像坐标系绘制。

Setup 11: 主循环

    while True:

获取帧数据并将其转换为彩色

        inPassthroughFrameLeft = passthroughImageLeftQueue.get()
        passthroughFrameLeft = inPassthroughFrameLeft.getFrame()
        leftFrame = cv2.cvtColor(passthroughFrameLeft, cv2.COLOR_GRAY2BGR)

        inPassthroughFrameRight = passthroughImageRightQueue.get()
        passthroughFrameRight = inPassthroughFrameRight.getFrame()
        rightFrame = cv2.cvtColor(passthroughFrameRight, cv2.COLOR_GRAY2BGR)
  1. inPassthroughFrameLeft = passthroughImageLeftQueue.get():从名为passthroughImageLeftQueue的队列获取一个帧。

  2. passthroughFrameLeft = inPassthroughFrameLeft.getFrame():从获取到的帧对象inPassthroughFrameLeft中提取帧数据。

  3. leftFrame = cv2.cvtColor(passthroughFrameLeft, cv2.COLOR_GRAY2BGR):使用OpenCV的cv2.cvtColor函数将灰度图像passthroughFrameLeft转换为彩色图像。这里使用的颜色转换代码cv2.COLOR_GRAY2BGR将灰度图像转换为BGR彩色图像。

  4. inPassthroughFrameRight = passthroughImageRightQueue.get():从名为passthroughImageRightQueue的队列获取另一个帧。

  5. passthroughFrameRight = inPassthroughFrameRight.getFrame():从获取到的帧对象inPassthroughFrameRight中提取帧数据。

  6. rightFrame = cv2.cvtColor(passthroughFrameRight, cv2.COLOR_GRAY2BGR):使用OpenCV的cv2.cvtColor函数将灰度图像passthroughFrameRight转换为彩色图像。这里使用的颜色转换代码cv2.COLOR_GRAY2BGR将灰度图像转换为BGR彩色图像。

获取跟踪的特征,并在帧上绘制这些特征

        trackedFeaturesLeft = outputFeaturesLeftQueue.get().trackedFeatures
        drawFeatures(leftFrame, trackedFeaturesLeft)

        trackedFeaturesRight = outputFeaturesRightQueue.get().trackedFeatures
        drawFeatures(rightFrame, trackedFeaturesRight)

这段代码从输出特征队列中获取跟踪的特征,并在帧上绘制这些特征。

  1. trackedFeaturesLeft = outputFeaturesLeftQueue.get().trackedFeatures:从名为outputFeaturesLeftQueue的输出特征队列中获取一个特征跟踪结果对象,并从中提取跟踪的特征。

  2. drawFeatures(leftFrame, trackedFeaturesLeft):调用名为drawFeatures的函数,并将左侧帧leftFrame和跟踪的左侧特征trackedFeaturesLeft作为参数传递进去,以在左侧帧上绘制特征点。

  3. trackedFeaturesRight = outputFeaturesRightQueue.get().trackedFeatures:从名为outputFeaturesRightQueue的输出特征队列中获取另一个特征跟踪结果对象,并从中提取跟踪的特征。

  4. drawFeatures(rightFrame, trackedFeaturesRight):调用名为drawFeatures的函数,并将右侧帧rightFrame和跟踪的右侧特征trackedFeaturesRight作为参数传递进去,以在右侧帧上绘制特征点。

显示帧图像

        cv2.imshow(leftWindowName, leftFrame)
        cv2.imshow(rightWindowName, rightFrame)

使用OpenCV的cv2.imshow函数显示左右两个帧。

  1. cv2.imshow(leftWindowName, leftFrame):使用OpenCV的cv2.imshow函数显示左侧帧。leftWindowName是左侧窗口的名称,用于标识窗口,leftFrame是要显示的帧。

  2. cv2.imshow(rightWindowName, rightFrame):使用OpenCV的cv2.imshow函数显示右侧帧。rightWindowName是右侧窗口的名称,用于标识窗口,rightFrame是要显示的帧。

对键盘输入响应程序

        key = cv2.waitKey(1)
        if key == ord('q'):
            break
        elif key == ord('s'):
            if featureTrackerConfig.cornerDetector.type == dai.FeatureTrackerConfig.CornerDetector.Type.HARRIS:
                featureTrackerConfig.cornerDetector.type = dai.FeatureTrackerConfig.CornerDetector.Type.SHI_THOMASI
                print("Switching to Shi-Thomasi")
            else:
                featureTrackerConfig.cornerDetector.type = dai.FeatureTrackerConfig.CornerDetector.Type.HARRIS
                print("Switching to Harris")

            cfg = dai.FeatureTrackerConfig()
            cfg.set(featureTrackerConfig)
            inputFeatureTrackerConfigQueue.send(cfg)

上面的代码实现了等待用户按下键盘上的键,并根据按下的键执行不同的操作。

  1. key = cv2.waitKey(1):等待用户按下键盘上的键,1代表等待1毫秒。此函数会返回按键的ASCII值。

  2. if key == ord('q')::如果按下的键是字母q对应的ASCII值,即用户按下q键:

    • break:跳出循环,结束程序。
  3. elif key == ord('s')::如果按下的键是字母s对应的ASCII值,即用户按下s键:

    • if featureTrackerConfig.cornerDetector.type == dai.FeatureTrackerConfig.CornerDetector.Type.HARRIS::检查当前角点检测器的类型是否是Harris角点检测器。

      • featureTrackerConfig.cornerDetector.type = dai.FeatureTrackerConfig.CornerDetector.Type.SHI_THOMASI:如果是Harris角点检测器,则将角点检测器类型切换为Shi-Thomasi角点检测器。

        • print("Switching to Shi-Thomasi"):打印消息,表示已切换到Shi-Thomasi角点检测器。
      • else::如果当前角点检测器的类型不是Harris角点检测器。

        • featureTrackerConfig.cornerDetector.type = dai.FeatureTrackerConfig.CornerDetector.Type.HARRIS:将角点检测器类型切换回Harris角点检测器。

        • print("Switching to Harris"):打印消息,表示已切换回Harris角点检测器。

    • cfg = dai.FeatureTrackerConfig():创建一个新的dai.FeatureTrackerConfig对象。

    • cfg.set(featureTrackerConfig):将当前的特征跟踪器配置featureTrackerConfig复制到新创建的对象中。

    • inputFeatureTrackerConfigQueue.send(cfg):将特征跟踪器配置对象发送到名为inputFeatureTrackerConfigQueue的队列中。

Setup 12:运行程序

在终端中输入如下指令运行程序

python main.py

运行程序,看下效果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/w137160164/article/details/131450709
今日推荐